结构体内存对齐

一.为什么要对齐?
 
《Windows核心编程》里这样说:当CPU访问正确对齐的数据时,它的运行效率最高,当数据大小的数据模数的内存地址是0时,数据是对齐的。例如:WORD值应该是总是从被2除尽的地址开始,而DWORD值应该总是从被4除尽的地址开始,数据对齐不是内存结构的一部分,而是CPU结构的一部分。当CPU试图读取的数值没有正确的对齐时,CPU可以执行两种操作之一:产生一个异常条件;执行多次对齐的内存访问,以便读取完整的未对齐数据,若多次执行内存访问,应用程序的运行速度就会慢。在最好的情况下,是两倍的时间,有时更长。
 
二.成员变量对齐的原理
 
内存对齐遵循两个原则:
  1. 成员的内存对齐。成员在内存中的起始位置必须是{其所需内存大小与编译器预设的一个字节数}取较小值的整数倍。
  2. 结构体整体的内存对齐。结构体整体所占内存大小需是{所有成员中占内存最大的与编译器预设的一个字节数}取较小的整数倍。
对于
struct s1
{
 
char a;
long int d;
double c;
};
 
成员a在内存0的位置;成员d大小为4,编译器预设的字节数为8,那么它按4字节对齐,所以存在偏移4-7的位置;c的大小为8,按8字节对齐,存在8-15的位置。整个结构体占16字节内存。由于内存成员最大为8,取它与编译器预设字节8较小值,因此结构体所占内存需调整为8的整数倍,而16刚好是8的整数倍,无需调整。因此,该结构体所占内存为16。
 
而对于
struct s2
 
{
 
char a;
 
long int d;
 
double c;
 
char e;
 
};
 
前三个成员和s1一样。成员e是1字节的,按1字节对齐(任何偏移位置都是1的整数倍),放在位置16。因此,整个结构体占17字节。同上,结构体按照8字节对齐,所以结构体占24字节内存。
 
 
当然你可以使用#pragma指令指定编译器按4字节对齐。即
 
#pragma pack(4)       // 这里也可以是#pragma pack(push,4)
 
struct s1
 
{
 
char a;
 
long int d;
 
double c;
 
};
 
struct s2
 
{
 
char a;
 
long int d;
 
double c;
 
char e;
 
};
 
 这时s1的大小还是16,所有成员存放的位置也和前边一样。不同的是,虽然成员c仍放在8-15的位置,但它是按4字节对齐而非原先的8字节,只是比较巧,内存偏移8既是8的倍数也是4的倍数。而且,最终结构体的对齐也是按4字节对齐而不是之前的8,只是16既是8的倍数也是4的倍数,因此无需调整。
s2的成员存放也同之前的情况,最终结构体占17字节内存。17按4字节对齐是20,所以此时s2占20字节的内存!
 
你也看到并不是指定编译器按4字节对齐就按4字节对齐的。比如下面的结构体:
 
#pragma pack(4)
 
struct TestStruct2
 
{
 
    char m1[11];
 
    short m2;
 
};
 
你知道它的大小吗?是14。因为m1按1字节对齐,存诸在0——10位置,m2按2字节对齐,存诸在12——13位置。结构体占用14个字节,因为结构体内最大的成员的数据类型是short,大小为2,比指定的对齐字节4小,所以14对2园整,得14。
 
 
总的说来就是结构成员的对齐是用成员大小和#pragma pack(push,n)中的n中较小的数对齐,例如如果成员大小为2,而你指定的对齐方式是4,则该成员按2对齐;结构本身的对其是用结构中最大成员的大小和#pragma pack(push,n)中的n较小的数对齐,即最后的园整,例如如果结构中最大成员大小8,而你指定对齐是16,则结构本身按8对齐。
原文地址:https://www.cnblogs.com/lulu10922/p/5803722.html