一、结构体
1.匿名结构体类型
我们来举个例子介绍匿名结构体
struct //创建时只有关键字,即为匿名结构体 { char a; int b; char c; double d; }s;
再来列举一个匿名结构体常见的错误
struct { char a; int b; char c; double d; }s; struct { char a; int b; char c; double d; }*p; //用p指向这个匿名结构体 int main() { p = &s; //观察两个匿名结构体发现组成成分一样,是不是就可以通过这种方式使 p 指针存储 s 呢? //error 虽然类型一样,但是在编译器看来这是两个不同的匿名结构体,非法 return 0; }
2.结构体的自引用
举个例子来看看结构体如何相互引用
struct A { int i; char c; }; struct B { char c; struct A sa; //结构体类型 struct B 中含有结构体类型 struct A; double b; };
那么结构体自引用是否为以下这种方式呢
struct N { int d; struct N n; //error 无限套娃 ,如果计算大小就直接爆炸 };
正确方法如下
struct N { int d; struct N* next; //通过指针找到同类型的下一节点 };
3.结构体的大小计算
结构体的对齐规则
1.第一个成员在与结构体偏移量为0的地址处 2.其他成员变量要对齐到(对齐数)的整数倍的地址处 . 对齐数 = 编译器默认的对齐数 与 该成员大小的较小值 VS默认为8(并且该默认值可以通过编码修改) . 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍 4.如果该结构体嵌套了另一个结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处 结构体整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
我们以一张图来整明白这些话
前面提到过可以修改默认对齐数
以下
#pragma pack() //括号内的数字即是你要的默认对齐数 / / //使用完结构体后再恢复默认对齐数为8= #pragma pack() //括号内不放数字
举个例子
#pragma pack(2) struct A { char a; int b; char c; }; #pragma pack() int main() { printf("%d\n",sizeof(struct A)); return 0; }
二、位段
1.位段的介绍
位段的声明和结构体是类似的,但有两个不同 1.位段的成员必须是int、unsigned int 或 signed int 2.位段的成员后边有一个冒号和一个数字
例如
struct A { int a:2; //a成员占2bit位 int b:5; //b成员占5bit位 int c:10; //c成员占10bit位 int d:30; //d成员占30bit位 } //此时A就是一个位段类型
为什么好好的一个整形,偏要把它硬塞进2bit呢?
节省空间!!
比如说表示性别,2个字节就足矣
00 | 保密 |
---|---|
01 | 女 |
11 | 男 |
2.位段的大小
还是以上述代码举例子
//2+5+10+30=47 //识别出int,创建一个int的大小,即32bit struct A { //创建4字节----32bit int a:2; //a成员占2bit位 int b:5; //b成员占5bit位 int c:10; //c成员占10bit位 //还留下15bit //d塞不进去了 再创建4字节 ---32bit int d:30; //d成员占30bit位 } int main() { printf("%d\n",sizeof(struct A)); //结果为4+4 = 8字节 return 0; }
但其实,在不同的环境下结果可能是不同的
比如在上述代码中,d塞不进去后,是继续用上一次留下的15bit,还是直接塞进新创建的呢?
所以说
位段涉及到很多不确定因素,位段是不跨平台的 注重可移植的程序应该避免位段
3.位段的实际使用情况
struct S { char a:3; char b:4; char c:5; char d:4; }; struct S s = {0}; s.a = 10; s.b = 12; s.c = 3; s.d = 4;
4.位段跨平台问题
1.int位段被当成有符号数还是无符号数是不确定的 2.位段中的最大位的数目不能确定(16位机器最大16,32位最大32,若写成大于16的数再16位机器就会出问题) 3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义 4.当一个结构体包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时 —是舍弃剩余的位还是利用,这是不确定的
三、联合(共用体)
1.联合类型的定义
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员 特征是这些成员共用一块空间(所以联合也叫共用体) . 所以联合体的大小最小是最大成员的大小
2.利用联合特征判断机器大小端字节序
int check_sys() { union U { char c; int i; }u; u.i = 1; // 00 00 00 01 // 01 00 00 00 return u.c; //返回1是小端 //返回0是大端 }
3.联合大小的计算
联合大小最小是最大成员的大小 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
比如
union Un { char arr[5]; //对齐数为1 int b; //对齐数为4 //又因为至少为5(arr为最大成员) //所以对齐至8 };