您好,欢迎来到刀刀网。
搜索
您的当前位置:首页高阶C语言之三:自定义类型

高阶C语言之三:自定义类型

来源:刀刀网


内存对齐相关代码:

位段与练习:

枚举和联合:

结构体

结构是一些值的集合,这些值称为成员变量。结构的每个成员变量可以是不同类型。

结构体的声明与定义
struct tag
{
	int x;
	char y;
	double z;
}s1,s2,s3;//这里的s1,s2都是创建的结构体变量

//匿名结构体类型
struct
{
	char name[10];
	int num;
}s1;//匿名结构体类型只能用一次

//定义
struct tag s;

即便两个匿名结构体的成员变量完全相同,编译器会把这两个声明当成完全不同的两个类型。

结构体的自引用

数据结构中最常用的。如:链表实现中节点的定义。

数据结构:数据在内存中的存储结构,包括线形、顺序表、链表、树形、二叉树。

struct Node
{
	int data;//4Byte
	struct Node* next;//x86:4Byte;x:8Byte
};

结构体的成员变量不能是同类型的结构体,只能是同类型的结构体指针。(以下为错误写法:)

struct Node
{
	int data;//4Byte
	struct Node next;
};
//这样的结构体发生嵌套,编译器会报错
 结构体的重命名
typedef struct Node
{
	int data;//4Byte
	struct Node* next;//x86:4Byte;x:8Byte
}Node,*LinkList;

//等价于
typedef struct Node Node;
typedef struct Node* LinkList;
结构体变量的初始化

1、成员变量里没有结构体类型。 

struct Point
{
	int x;
	int y;
};

int main()
{
	//初始化:在创建的同时赋值
    struct Point p1 = { 2,3 };
	return 0;
}

2、成员变量里面有结构体类型。

struct stu
{
	char name[10];
	int age;
};

struct score
{
	int n;
	char ch;
	struct stu;
};

int main()
{
	struct score sc1= { 1,'c',{"zhangsan",18} };
	return 0;
}
  结构体传参
void print1(struct S ss); //传值
void print2(const struct S* ps);//传地址

结构体内存对齐

!!!非常重要!!!

 相同成员变量的结构体,所占内存空间的大小并不完全相同。

 结构体对齐规则

对齐数 =   min( 编译器默认的对齐数 , 该成员的字节大小 )

  •  vs默认的对齐数为8。
  • 其他编译器没有默认对齐数,数据的对齐数即本身大小。

 宏:offsetof

头文件<stddef.h>

size_t offsetof( structName, memberName );
//structName:结构体名
//memberName:成员变量名
//返回值:该成员变量相对于起始地址的偏移量
int main()
{
	printf("%d\n", offsetof(struct S1, c1));//0
	printf("%d\n", offsetof(struct S1, i));//4
	printf("%d\n", offsetof(struct S1, c2));//8
	return 0;
}
 结构体对齐的练习

1、相同成员变量,不同的对齐方式所占空间大小不同。

2、结构体本事的最大对齐数为:8(为下面的嵌套铺垫)

 3、嵌套结构体的内存对齐

内存对齐的意义

1、平台原因

2、性能原因

        数据结构应尽可能地在自然边界上对齐。

  • 访问未对齐的内存,处理器需要两次内存访问。
  • 访问对齐的内存,处理器只需要一次内存访问。

3、总结

        结构体的内存对齐就是拿空间换时间的做法。

4、设计结构体的方法

        尽可能让占用空间小的成员集中在一起。

 默认对齐数的修改
//#pragma once
//头文件中使用,功能:防止头文件被多次引用

#pragma pack(4)//修改默认对齐数
struct S
{
	int i;
	double d;
};
//默认对齐数为8:占16字节
//默认对齐数为4:占12字节
#pragma pack()//取消修改

//pragma pack每次使用最好设置范围

位段

位段通过结构体来实现,位段是用来节省空间的

位段的声明和结构体类似,但有两个不同:

1、位段的成员名必须是整型家族的类型(char、int、unsigned int等)。

2、位段的成员名后面有一个冒号和一个数字。

struct A
{
	//冒号后面的bit位
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
//2+5+10+30=47 
//6 Byte =  48 bit
// 实际大小
//8 Byte =   bit
位段的内存分配
  • 位段的成员可以是char、int、unsigned int,必须整型家族类型的。
  • 位段的空间是按照需要4个字节(int)或者1个字节(char)的方式来开辟的。
  • 位段涉及很多不稳定因素,是不可跨平台的

 位段的跨平台问题

  1. int位段被当做有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不确定。
  3. 位段的成员在内存中从左向右分配,还是从右向左分配,标准未定义。
  4. 当一个结构体中包含两个位段,第二个位段成员较大,无法容纳第一个位段的剩余位时,是舍弃剩余的位还是利用这是不确定的。

        不同的编译器、机器等对于位段的实现没有统一标准,但这并不意味着位段没有任何意义。位段本身并不跨平台,但是我们可以用不同的代码来实现位段在平台应用。

枚举

 枚举:把实际存在的数据抽象出来

枚举类型的定义
enum Day
{
	Mon=1,//枚举常量
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
//枚举常量从第一个开始一次递增1
//默认第一个枚举常量为0
枚举的优点

为什么用枚举而不用#define?

  1. 增加了代码的可读性和维护性。
  2. 比起#define定义的标识符,枚举有类型检查,更加严谨。
  3. 防止命名污染。
  4. 便于调试。
  5. 使用方便,一次可以定义多个变量。

#define语句在预处理阶段就会被替换。在调试时的代码已经不是看到的代码了。

联合(共用体)

  1. 联合也是一种特殊的自定义类型,
  2. 这中类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间
联合的定义
union Un
{
	int a;
	char c;
};
//占4个字节
//联合的任意两个成员不能同时使用
 联合体的特点
  • 联合体的成员共用同一块内存空间。
  • 一个联合体的大小,至少是最大成员的大小。
  • 任意两个成员不能同时被使用
联合体的应用

1、判断大小端字节序

int check_sys_u()
{
	union Un
	{
		char c;
		int i;
	}u;
	u.i = 1;
    //返回1是小端,返回0是大端
	return u.c;
}
联合体大小的计算
  •  至少是最大成员的大小。

  • 当最大成员不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍。(类似结构体内存对齐)。

union U
{
	//char arr[5];//对齐数是 char=1
	short arr[7];//对齐数是 short=2
	int i;
	//对齐
};//16个字节

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.com 版权所有 湘ICP备2022005869号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务