转载自https://blog.youkuaiyun.com/yanhang1589/article/details/46008583
C++中字节的对齐方式可以采用编译器默认的字节对齐方式, 也可以采用自定义的对齐方式。
编译器默认字节对齐方式
例1:
- struct test1
- {
- char c1; //c1长度1个字节, 采用1字节对齐
- short s; // s长度为2个字节,采用2字节对齐
- int i; // i长度为4个字节,采用4字节对齐
- char c2; //c2长度1个字节,采用1字节对齐
- };
sizeof(test1)的结果为12。
test1 的内存分布如下(以1表示占用字节,*表示空字节)
c1 | s | i | c2 |
1* | 11 | 1111 | 1*** |
原因如下:
- 成员c1,其偏移地址为0,占据了第1个字节。此时共分配了1个字节。
- 成员s为short类型,占用2个字节,采用2字节对齐。已经分配的1个字节不是2的整数倍,所以编译器在c1与s之间插入1个空字节,然后为s分配2个字节。此时共分配了4个字节。
- 成员i为int类型,占用4个字节,采用4字节对齐。已经分配的4个字节是4的整数倍,所以编译器在i与s之间不插入空字节,然后直接为i分配4个字节。此时共分配了8个字节。
- 成员c2为char类型,占用1个字节,采用1字节对齐。已经分配的8个字节是1的整数倍,所以编译器在c2与i之间不插入空字节,然后直接为c2分配1个字节。此时共分配了9个字节。
- 此时所有的成员已经分配完了,但test1是采用4字节对齐的(请查看第2点),9不是4的整数倍(请查看第3点),故在c2之后插件3个空字节以使test1对齐。此时共分配了12个字节,所以Sizeof(test)的结果为12。
例2:
- struct test2
- {
- char c1; // 1字节对齐
- test1 t1; // 4字节对齐
- short s; // 2字节对齐
- };
根据第2点知道test1的对齐方式是4字节对齐。所以test2的对齐方式是t1字节的对齐方式,即4字节对齐。
sizeof(test2)的结果为20。
test2 的内存分布如下(以1表示占用字节,*表示空字节)
c1 | t1 | s |
1*** | 111111111111 | 11** |
3、对齐后的长度必须是该类型的对齐字节数的整数倍。
自定义字节对齐方式
使用伪指令#pragma pack(__attribute((aligned (n)))也可以实现, 这里不讨论)可以实现字节对齐方式设置。
设置自定义成员对齐后,同样要满足每个成员按自己的方式对齐,也就是说虽然指定对齐字节数,但并不是所有的成员都以指定对齐字节数来对齐。其对齐的规则是:每个成员按其类型的长度和指定对齐字节数中较小的一个对齐。例如变量var的长度是4个字节, 自定义对齐字节长度为2(或8), 则var的对齐字节数为2(或4)。
使用格式如下(建议:使用时最好相互匹配):
1、#pragma pack(n)与#pragma pack ()
- #pragma pack(n) // 编译器将按照n个字节对齐。
- #pragma pack() // 取消自定义字节对齐方式(取消后恢复到编译器默认的对齐方式)。
- #pragma pack(2)
- struct test3 // test3采用2字节对齐(满足默认对齐中的第2点)
- {
- char c1; //c1长度1个字节, 采用1字节对齐
- short s; // s长度为2个字节,采用2字节对齐
- int i; // i长度为4个字节,大于自定义的2字节,故采用2字节对齐
- char c2; //c2长度1个字节,采用1字节对齐
- float f; // f长度4字节,大于自定义的2字节,故采用2字节对齐
- double d; //d长度8字节,大于自定义的2字节,故采用2字节对齐
- char c3; //c2长度1个字节,采用1字节对齐
- };
- #pragma pack()
sizeof(test3)的结果为24。
test3 的内存分布如下(以1表示占用字节,*表示空字节)
c1 | s | i | c2 | f | d | c3 |
1* | 11 | 1111 | 1* | 1111 | 11111111 | 1* |
原因如下:
- 成员c1,其偏移地址为0,占据了第1个字节。此时共分配了1个字节。
- 成员s为short类型,占用2个字节,大于等于自定义的2字节,故采用2字节对齐。已经分配的1个字节不是2的整数倍,所以编译器在c1与s之间插入1个空字节,然后为s分配2个字节。此时共分配了4个字节。
- 成员i为int类型,占用4个字节,大于等于自定义的2字节,故采用2字节对齐。已经分配的4个字节是2的整数倍,所以编译器在i与s之间不插入空字节,直接为i分配4个字节。此时共分配了8个字节。
- 成员c2为char类型,占用1个字节,小于等于自定义的2字节,故采用1字节对齐。已经分配的8个字节是1的整数倍,所以编译器在c2与i之间不插入空字节,直接为c2分配1个字节。此时共分配了9个字节。
- 成员f为float类型,占用4个字节,大于等于自定义的2字节,故采用2字节对齐。已经分配的9个字节不是2的整数倍,所以编译器在c2与f之间插入1个空字节,然后为f分配4个字节。此时共分配了14个字节。
- 成员d为double类型,占用8个字节,大于等于自定义的2字节,故采用2字节对齐。已经分配的14个字节是2的整数倍,所以编译器在d与f之间不插入空字节,直接为d分配8个字节。此时共分配了22个字节。
- 成员c3为char类型,占用1个字节,小于等于自定义的2字节,故采用1字节对齐。已经分配的22个字节是1的整数倍,所以编译器在c3与d之间不插入空字节,直接为c3分配1个字节。此时共分配了23个字节。
- 此时所有的成员已经分配完了,但test3是采用2字节对齐的,23不是2的整数倍,故在c2之后插件1个空字节以使字节对齐。此时共分配了24个字节,所以sizeof(test3)的结果为24。
如果test采用编译器默认对齐方式,则sizeof(test)的结果为32,此处省略原因。
2、#pragma pack (push,n)与#pragma pack(pop)
- #pragma pack (push,n) // 把原来对齐方式设置压栈,并设新的对齐方式为n个字节对齐
- #pragma pack(pop) // 恢复原来对齐方式
从上面两点来看#pragma pack(n)与#pragma pack (push,n)来看,他们之间的区别并不大,可以相互代替,只是后者恢复时是恢复到了原来的对齐方式,而前者是恢复到了编译器默认的对齐方式。推荐使用后者。
例4:#pragma pack(n)与#pragma pack (push,n)的混合使用
- #include <stdio.h>
- // 采用默认的字节对齐方式, 即t1中最长的类型int, 即4字节对齐
- struct t1
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- #pragma pack(2) // 编译器将按照2个字节对齐
-
- // 2字节对齐
- struct t2
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- #pragma pack(push, 1) // 把原来对齐方式(2字节)置压栈,并设新对齐方式为1字节
-
- // 1字节对齐
- struct t3
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- #pragma pack(2) // 编译器将按照2个字节对齐
-
- // 2字节对齐
- struct t4
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- #pragma pack() // 取消自定义字节对齐(2字节)方式, 采用默认字节对齐方式
-
- // 采用默认的字节对齐方式, 即t5中最长的类型int, 即4字节对齐
- struct t5
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
-
- #pragma pack(pop) // 取消1字节对齐方式。恢复到原来的2个字节对齐方式
-
- // 2字节对齐
- struct t6
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- #pragma pack() // 取消自定义字节对齐(2个字节)方式, 采用默认的字节对齐方式
-
- // 采用默认的字节对齐方式, 即t7中最长的类型int, 即4字节对齐
- struct t7
- {
- char c;
- int i;
- short s;
- float f;
- char c1;
- };
-
- int main()
- {
- printf("sizeof(char): %d\n", sizeof(char));
- printf("sizeof(short): %d\n", sizeof(short));
- printf("sizeof(int): %d\n", sizeof(int));
- printf("sizeof(float): %d\n", sizeof(float));
-
- printf("\n=======================================\n\n");
-
- printf("sizeof(t1): %d\n", sizeof(t1));
- printf("sizeof(t2): %d\n", sizeof(t2));
- printf("sizeof(t3): %d\n", sizeof(t3));
- printf("sizeof(t4): %d\n", sizeof(t4));
- printf("sizeof(t5): %d\n", sizeof(t5));
-
- printf("sizeof(t6): %d\n", sizeof(t6));
- printf("sizeof(t7): %d\n", sizeof(t7));
-
- return 0;
- }
-
- /* 输出结果:
- sizeof(char): 1
- sizeof(short): 2
- sizeof(int): 4
- sizeof(float): 4
- =======================================
- sizeof(t1): 20
- sizeof(t2): 14
- sizeof(t3): 12
- sizeof(t4): 14
- sizeof(t5): 20
- sizeof(t6): 14
- sizeof(t7): 20
- 请按任意键继续. . .
- */