结构体为什么要进行字节对齐?主要原因是加快变量在内存的存取速度.我查了相关讨论对齐规则的资料,决定自己整理一下.
本文中基本数据类型占据大小分别为sizeof(char)=1.sizeof(short)=2,sizeof(int)=4,sizeof(float) =4,sizeof(double)=8,测试环境win64+VS2013
对齐主要有两方面,一个是结构体里面的变量字节对齐,一个是结构体本身再进行对齐.
基本对齐
1.变量对齐
结构体中的变量以变量类型在内存中所占据的的字节数为对齐依据,如int占据4个字节,那么int类型变量在结构体中就按照4个字节对齐.
struct stAlign
{
char value1;
int value2;
};
sizeof(stAlign) = 8.
value1是char类型,占据一个字节,value2是int类型,占据4个字节,需要从4的整数倍地址开始分配,于是会从value1后面偏移3个字节的地址开始分配.
2.结构体对齐
struct stAlign3
{
char value1;
int value2;
char value3;
};
sizeof(stAlign3)=12.
value1占据1个字节,value2先偏移3个字节然后占据4个字节.value3因为char本身就占据1个字节所以不需要偏移,3个变量一共占据9个字节,变量对齐完后,轮到结构体本身进行对齐,先找结构体中占据字节最大的类型,然后让结构体整个占据的大小为该类型的整数倍,stAlign3里面最大的类型为int,占据4个字节,让9匹配到4的整数倍,就是12.同理,如果把int换成double的话,整个结构体占据24个字节.
改变对齐方式
C++中可以通过一些预编译指令来改变结构体的字节对齐方式
1.#pragma pack指令
#pragma pack(push,n)
#pragma pack(pop)
另一对指令同样可以达到改变字节对齐规则的目的
#pragma pack(n)
#pragma pack()
在进行变量对齐时,把变量本身占据的字节和n进行比较,取其中比较小的那个作为对齐依据,如果n大于变量本身占据的字节,则按照自身大小进行对齐,这就是pack的意义,pack即压缩打包,自然是往小的方向对齐。
#pragma pack(push,4)
struct stAlign5
{
char value1;
short value2;
double value3;
};
#pragma pack(pop)
sizeof(stAlign5) = 12
value1占据一个字节,value2是short类型,占据2个字节,把2和4进行对比,取较小的2作为对齐依据,所以偏移量为1,value3是double类型,占据8个字节,把8和4进行对比,取较小的4作为对齐依据,偏移量为0,所以整个占据12个字节.如果没有用pragma指令的话,那么占据的是16个字节.
结构体对齐时,取结构体中占据字节最大的基础类型和n进行对比,取较小的作为对齐依据.
#pragma pack(push,4)
struct stAlign6
{
char value1;
double value2;
char value3;
};
#pragma pack(pop)
按照之前的规则,value1和value2占据12个字节,value3占据1个字节,一共13个字节,这时结构体本身对齐时,结构体中最大的为double,占据8个字节,把8和4对比,取较小的4作为对齐依据,所以占据16个字节,如果没用pragma指令的话, 占据的是24个字节.
对于#pragma pack()指令,简单地说,它指定对齐时两者相比取其小(对于变量而言,拿变量类型占据的大小和n对比,对于结构体而言,拿结构体中最大的基本数据类型占据的大小和n进行比较),大多数情况下,该指令指定结构体往小的方向对齐,目的是为了压缩结构体所占的内存空间.
2._deslspec(align(n))指令
如果使用该指令,那么对于变量来说,对齐的字节大小取变量类型大小与pack(n)与align(n)的最小值,对于结构体来说,对齐的依据是align(n)与结构体中最大成员对齐依据取其大.如果一个成员b是复合结构,以b的对齐依据为准而不是它实际占用的大小.
#pragma pack(push,2)
_deslspec(align(32))
struct stAlign9
{
int value1;
double value2;
char value3;
int value4;
};
#pragma pack(pop)
sizeof(stAlign9) = 32
align(32)与double为8对比取其大,故以32为对齐依据,stAlign9算完成时占据18,最后往32的整数倍对齐,最终占32个字节.
struct st1
{
double value1;
double value2;
double value3;
};
sizeof(st1) = 24
_deslspec(align(16))
struct st2
{
char value1;
st1 value2;
};
sizeof(st2)=32
最终st2对齐时,取16和value2的对齐8较大的16为依据,所以是32.
3.结构体中含有结构体
如果A结构体中含有B结构体变量,那么最后对A结构体对齐考虑B时,要以B结构体最终的对其依据为参数.
不管一个结构体施加了pragma或者align或者没有,结构体在最终对齐时都有一个对齐依据,指令只是改变了这个对齐依据而已.
#对齐指令
struct B
{
...
};
struct A
{
B b;
};
;
假设B最终结构体的对齐为X,那么对于A中的b来说,对齐依据就是X,而不是sizeof(B)
例
struct B1
{
int value1;
};
sizeof(B1) = 4; //最终对齐依据是4
struct A1
{
char value1;
B1 value2;//以4为对齐依据
};
sizeof(A1) = 8;
例
_deslspec(align(32))
struct B2
{
int value1;
};
sizeof(B2) = 32; //最终对齐依据是32
struct A2
{
char value1;
B2 value2;//以32为对齐依据
};
sizeof(A2) = 64;
例
#pragma pack(2)
struct B3
{
char value1;
int value2;
};
sizeof(B3) = 6; //最终对齐依据为2
#pragam pack()
struct A3
{
char value1;
B3 value3;//以2为对齐依据
};
sizeof(A3) = 8