1.结构体内存对齐
(1)结构体内存对齐规则
a.第一个成员在与结构体变量偏移量为0的地址处
b.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数=编译器默认对齐数与该成员大小的较小值.
vs中默认值为8(如果没有默认对齐数 则默认对齐数为成员大小本身)
c.结构体总大小为最大对齐数的整数倍
d.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体大小就是所有最大对齐数(含嵌套结构体)的整数倍
(2)举例:
a.
int main() { struct S1 { char c1; //1 8 对齐数为1 1字节 int i; //4 8 对齐数为4 要在偏移量4的整数倍处开辟 浪费3个字节 8字节 char c2; //1 8 对齐数为1 9字节 最后结构体大小要为最大对齐数4的整数倍 所以又浪费3字节 12字节 }; struct S1 s; }
b.
#include<stdio.h> int main() { struct S2 { char c1;//1 8 对齐数为1 1字节 char c2;//1 8 对齐数为1 2字节 int i; //4 8 对齐数为4 要在偏移量为对齐数的整数倍处开辟 浪费两个字节 8字节 }; struct S2 s; }
c.
int main() { struct S3 { double d;// 8 8 对齐数为8 8字节 char c; // 1 8 对齐数为1 9字节 int i; // 4 8 对齐数为4 在12处开辟 16字节 }; struct S3 s; }
d.
int main() { struct S3 { double d;// 8 8 对齐数为8 8字节 char c; // 1 8 对齐数为1 9字节 int i; // 4 8 对齐数为4 在12处开辟 16字节 }; struct S4 { char c1; //1 8 对齐数为1 1字节 struct S3 s3;//8 8 嵌套结构体对齐数为内结构体的最大对齐数8 在8处开辟8+16=24 double d; //8 8 对齐数为8 8+24=32 32字节 结构体大小为最大对齐数的整数倍 }; struct S4 s; }
(3)内存对齐的原因
a.平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据。
b.性能原因
为了访问未对齐的内存,处理器要做两次内存访问。而对齐后的内存,处理器只需要访问一次。
(4)总结
如果要使结构体尽可能少的浪费空间,我们需要让占用空间小的成员尽量集中在一起。
2.位段
1.什么是位段?
位段的声明和结构体是类似的,用两个不同:
a.位段的成员必须是:int ,unsigned int,signed int,char , unsigned char。
b.位段的成员名后边有一个冒号和一个数字。
int main() { struct A { int _a : 2; //_a这个成员只占2个bit位 只能表示0 1 2 3 int _b : 5; //_b这个成员只占5个bit位 int _c : 10; int _d : 30; }; }
2.位段的空间是按照需要以4个字节或一个字节来开辟的
3.弊端:位段是不跨平台的.
3.枚举(一一列举)
利用枚举类型定义常量(比宏定义有优点)
int main() { enum Color { RED = 5, GREEN = 8, BULE }; //一次可以定义多个常量 }
4.联合(共用体)
这些成员共用一块空间(同一时间只能使用一个!)
int main() { union Un { char c;//c用的是i的第一个字节的空间 int i; }; union Un un; //联合体大小至少是最大成员的大小 }
1.联合体的大小(要对齐)
int main() { union Un { short arr[7];// 14 2 int i;//4 4 }u;//本来14个一节就够了 但是联合体需要对其 //到最大对齐数4的整数倍 所以为 16个字节 }
2.利用联合体判断大小端
#include<stdio.h> int main() { union Un { char c; int i; }u; u.i = 1; if (u.c == 1) { printf("小端\n"); } else { printf("大端\n"); } }