结构体对齐方式
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。
语法:#pragma pack( [show] | [push | pop] [, identifier], n )
对齐的算法: 由于各个平台和编译器的不同,可能会有所不同,本文是在window 2008 32位系统 vs2005上尝试的。
在不指定对齐方式时 编译器默认大小为8
可以通过命令 #pragma pack (show) 查看
那就从默认的开始说起,由于默认是8 ,结构体如下
typedef struct TEST_S
{
char b;
int a;
short c;
};
sizeof(TEST_S) = ? 应该是12,为何?
原因:
假设TEST_S从地址空间0x0000开始存放。没有定义指定对齐值,该值默认为8。
第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值8小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.
第二个成员变量a,其自身对齐值为4,比指定或者默认指定对齐值8小,所以其有效对齐值为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%4=0, 且紧靠第一个变量。
第三个变量c,自身对齐值为2,比指定或者默认指定对齐值8小,所以其有效对齐值为2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。
再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;
==================分割线================================
重新指定对齐大小
#pragma pack(push)
#pragma pack(2)
typedef struct TEST_S
{
char b;
int a;
short c;
};
#pragma pack(pop)
sizeof(TEST_S) = ? 应该是8,为何?
原因:
假设TEST_S从地址空间0x0000开始存放。指定对齐值为2。
第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值2小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.
第二个成员变量a,其自身对齐值为4,比指定或者默认指定对齐值2大,所以其有效对齐值为2,所以只能存放在起始地址为0x0002到0x0005这四个连续的字节空间中,符合0x0002%2=0, 且紧靠第一个变量。
第三个变量c,自身对齐值为2,与指定或者默认指定对齐值2一样,所以其有效对齐值为2,可以存放在0x0006到0x0008这两个字节空间中,符合0x0006%2=0。所以从0x0000到0x0008存放的都是B内容。
再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,但结构体的有效对齐值是2。根据结构体圆整的要求,0x0008到0x0000=8字节,(8)%2=0。所以B从0x0000到0x0008共有8个字节,sizeof(struct B)=8;
==================分割线================================
重新指定对齐大小
#pragma pack(push)
#pragma pack(1)
typedef struct TEST_S
{
char b;
int a;
short c;
};
#pragma pack(pop)
sizeof(TEST_S) = ? 应该是7,为何?
原因:
假设TEST_S从地址空间0x0000开始存放。指定对齐值为1。
第一个成员变量b的自身对齐值是1,与指定或者默认指定对齐值1一样,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.
第二个成员变量a,其自身对齐值为4,比指定或者默认指定对齐值1大,所以其有效对齐值为1,所以只能存放在起始地址为0x0001到0x0004这四个连续的字节空间中,符合0x0001%1=0, 且紧靠第一个变量。
第三个变量c,自身对齐值为2,比指定或者默认指定对齐值1大,所以其有效对齐值为1,可以存放在0x0005到0x0007这两个字节空间中,符合0x0005%1=0。所以从0x0000到0x0007存放的都是B内容。
再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,但结构体的有效对齐值是1。根据结构体圆整的要求,0x0007到0x0000=7字节,(7)%1=0。所以B从0x0000到0x0007共有7个字节,sizeof(struct B)=7;
=======================分割线==================
现在思路是否清晰一些呢,现在如果在增加一个结构体成员 在看看什么效果
#pragma pack(push)
#pragma pack(2)
typedef struct TEST_S
{
char b;
char e;
int a;
short c;
};
#pragma pack(pop)
sizeof(TEST_S) = ? 应该是8,为何?
增加了一个成员 在长度没变。如果指定对齐大小为4呢 ?sizeof(TEST_S) = ? 应该是12
根据上面的理论应该都可以推算出来的~
总结一下:
1:如果没指定对齐大小 ,那就按照默认大小对齐,默认值大小为多少可以通过命令查看,前面已经提到了。
2:如果指定了对齐大小,那就要看结构体里的成员大小与指定大小比较,按照值较小的对齐。
3:计算大小的同时 要注意结构体的取整要求。