` C语言-结构体对齐详解 朱有鹏 1、结构体为何要对齐访问 访问结构体元素时需要对齐访问,主要是为了配合硬件,也就是说硬件本身有物理上的限制,因为对齐排布和访问可以提高访问效率。 如: struct s { char c; int b; };
内存本身是一个物理器件(DDR内存芯片,SoC上的DDR控制器),本身有一定的局限性:如果内存每次访问时按照4字节对齐访问,那么效率是最高的;如果你不对齐访问效率要低很多。 还有很多别的因素和原因,导致我们需要对齐访问。譬如Cache的一些缓存特性,还有其它硬件(譬如MMU、LCD显示器)的一些内存依赖特性,所以会要求内存对齐访问。对比对齐访问和不对齐访问,似然对齐访问牺牲了内存空间,换取了速度性能,而非对齐访问则是牺牲了访问速度,换取了内存空间的利用率。 2、结构体对齐的规则和运算 (1)结构体对齐实例分析 编译器本身可以设置内存对齐的规则,有以下的规则需要记住: 第一个:32位编译器,一般编译器默认对齐方式是4字节对齐。 #include struct mystruct1 { // 1字节对齐 4字节对齐 int a; // 4 4 char b; // 1 2 short c; // 2 2 }; int main(void){ // 4字节对齐时 printf("sizeof (struct mystruct1) = %d.
", sizeof (struct mystruct1)); // sizeof (struct mystruct1) = 8 }
分析:整个结构体变量4字节对齐是由编译器保证的,我们不用操心。第一个元素a的第一个字节的地址就是整个结构体的起始地址,所以自然是4字节对齐的。但是a的结束地址要由下一个元素说了算。然后是第二个元素b,因为上一个元素a本身占4字节,本身就是对齐的。所以留给b的开始地址也是4字节对齐地址的,所以b可以直接放,b放的位置就决定了a一共占4字节,因为不需要填充。b的起始地址定了后,结束地址不能定(因为可能需要填充),结束地址要由一个元素来定。然后是第三个元素c,short类型需要2字节对齐,short类型元素必须放在类似0,2,4,8这样的地址处,不能放在1,3这样的奇数地址处,因此c不能紧挨着b来存放,解决方案是在b之后添加1字节的填充(padding),然后再开始放c。c放完之后还没结束,当整个结构体的所有元素都对齐存放后,还没结束,因为整个结构体大小还要是4的整数倍。 (2)结构体对齐总结 第一:当编译器将结构体设置为4字节对齐时,结构体整体必须从4字节对齐处存放,结构体对齐后的大小必须4的倍数,如果编译器设置为8字节对齐,则这里的4就是8。 第二:结构体中每个元素本身都也必须对齐存放。 第三:编译器在考虑以上两点情况下,实现以最少内存来开辟结构体空间。 3、手动对齐 如果编译器自动实现结构体对齐,我们就称为自动对齐,与之相反,使用#pragma进行对齐的就是手动对齐。 #pragma备用告诉编译器,程序员自己希望的对齐方式。比如,虽然编译器的默认对齐方式是4,但是如果我们不希望按照4对齐,而是希望实现别的对齐方式,譬如希望1字节对齐,也可能希望是8,甚至可能希望128字节对齐,这个时候就必须使用#pragma进行手动对齐了。 常用的设置手动对齐的命令有两种:第一种是#pragma pack(),这种就是设置编译器1字节对齐,不过也可以认为是设置为不对齐或者取消对齐;第二种是#pragma pack(4),这个括号中的数字表示希望以多少字节进行对齐。 我们需要#prgama pack(n)开头,以#pragma pack()结尾,定义一个区间,这个区间内的对齐参数就是n。 include #pragma pack(4) // 4字节对齐 struct mystruct1 { int a; char b; short c; }; struct mystruct2 { char a; int b; short c; }; typedef struct myStruct5 { int a; struct mystruct1 s1; double b; int c; }MyS5; struct stu { char sex; int length; char name[10]; }; #pragma pack() int main(void) { printf("sizeof(struct mystruct1) = %d.
", sizeof(struct mystruct1)); printf("sizeof(struct mystruct2) = %d.
", sizeof(struct mystruct2)); printf("sizeof(struct mystruct5) = %d.
", sizeof(MyS5)); printf("sizeof(struct stu) = %d.
", sizeof(struct stu)); return 0; 分析:本例中四个结构体对齐结果: (1).struct mystruct1: 1字节对齐 2字节对齐 4字节对齐 8字节对齐 4 4 4 4 1 2(1+1) 2(1+1) 2(1+1) 2 2 2 2 (2). sizeof(struct mystruct2): 1字节对齐 2字节对齐 4字节对齐 8字节对齐 1 2(1+1) 4(1+3) 4(1+3) 4 4 4 4 2 2 4(2+2) 4(2+2) (4). MyS5(struct myStruct5): 1字节对齐 2字节对齐 4字节对齐 8字节对齐 4 4 4 4 7 8 8 8 8 8 8 8 4 4 4 4 (4). struct stu: 1字节对齐 2字节对齐 4字节对齐 8字节对齐 1 2(1+1) 4(1+3) 4(1+3) 4 4 4 4 10 10 12(10+2) 12(10+2) 对于#prgma pack的手动对齐来说,在很多C环境下都是支持的,自然gcc也支持,如果不是有哦特殊需求的话,不建议使用。
`
|