C中结构体内存对齐
以下的内容C++中也一样。
结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。
一、为何要内存对齐
因为处理器读写数据,并不是以字节为单位,而是以块(2,4,8,16字节)为单位进行的。如果不进行对齐,那么本来只需要一次进行的访问,可能需要好几次才能完成,并且还要进行额外的merger或者数据分离。导致效率低下。更严重地,有些架构的CPU会因为不允许访问unaligned address,就会报错.
二、内存对齐
结构体的内存布局依赖于CPU、操作系统、编译器及编译时的对齐选项。结构体内部成员的对齐要求,结构体本身的对齐要求。
(一)成员对齐。对于结构体内部成员,通常会有这样的规定:各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。(偏移量是size的倍数)
(二)整个结构体的对齐需求。要求结构体至少是其中的那个最大的元素大小的整数倍。因为有时候我们使用的是结构体数组,所以结构体的大小还得保证结构体数组中各个结构体满足对齐要求,同时独立的结构体与结构体数组中单个结构体的大小应当是一致的。(结构体size是max的倍数)
(三)编译器的对齐指令。VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏 移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条 件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数。(成员对齐:min(default, n), 结构体对齐:n>max按默认结构体对齐,小于等于不知道--)
1. 典型例子
struct MyStruct { char dda; //偏移量为0,满足对齐方式,dda占用1个字节; double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8的倍数,需要补足7个字节才能使偏移量变为8(满足对齐方式), //因此VC自动填充7个字节,dda1存放在偏移量为8 //的地址上,它占用8个字节。 int type; //下一个可用的地址的偏移量为16,是sizeof(int)=4的倍数,满足int的对齐方式, //所以不需要VC自动填充,type存 //放在偏移量为16的地址上,它占用4个字节。 }; //所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数, // 所以需要填充4个字节,以满足结构的大小为sizeof(double)=8的倍数。
所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。
2. 成员变量含结构体
#include<stdio.h> typedef struct node { char a; double b; int c; }Node; typedef struct node2 { char a; Node b; // 只需把b展开,且b的起始地址必须是b中最大元素size的倍数 }Node2; int main() { Node t1; printf("%d\n", sizeof(t1)); Node2 t2; printf("%d\n", sizeof(t2)); }
所以t2总的大小为(1+7)+(1+7+8+4) + 对齐 = 28 + 4 = 32.
3. 使用编译器的对齐指令
#pragma pack (2) /*指定按2字节对齐*/
参考链接: