C结构体的内存对齐
术语说明
pack 可以指定内存对齐值,单位是字节,这个是值需要时2的次幂(1,2,4,6,8)。如不设置也有默认值,这个值我理解的是操作系统的字长。
#pragma pack(8)
对齐规则
- 结构体第一个成员放在struct offset 0 的位置。
- 计算每个成员的对齐值,计算方式:min(size(成员), 默认对齐值)。
- 计算总体的对齐值,计算方式:min(max(所有成员的大小), 默认对齐值)。
详解
上面的说明较为笼统,下面使用一个例子来解释。
# include <stdio.h>
# pragma pack(8) # 手动设置对齐值。如不手动设置这个值,不同环境这个值不同,不方面我们接下来的计算。
struct Tag1 {
double a;
int b;
char c;
};
- 首先将double类型的a,存放在Tag1结构体offset=0的地址上,double类型长度是8,更新offset是8.
- 计算int b变量的对齐值,min(size(int), 默认对齐值) = min(4, 8) = 4, 然后判断offset是4的正整数倍数,显然是。将b变量放置在9的位置,此时offset变更成12.
- 计算char c变量的对齐值,min(size(char), 默认对齐值) = 1, 然后判断offset是不是4的整数倍呢?12是1的正整数倍,将c变量放置在13位置。
- 最后,计算总的对齐值,min(max(double、int、char), 默认对齐值) = min(8, 8) = 8, 13不是8的正整数倍数,填充到16。
- 最终 C语言的sizeof(struct Tag1) 的最终值是 16.
接下来将double 与 int的顺序修改下
# include <stdio.h>
# pragma pack(8) # 手动设置对齐值。如不手动设置这个值,不同环境这个值不同,不方面我们接下来的计算。
struct Tag1 {
int b;
double a;
char c;
};
- 首先将int类型b,放在Tag1结构体offset=0的地址上,更新offset=4.
- 接下来计算变量double a的对齐值,min(8, 8) = 8, 判断offset=4不是8的整数倍,填充到16(为什么是16?因为需要是8的最小正整数倍),此时offset=16。
- 接下来计算变量char c的对齐值,min(1, 8) = 1, 判断offset=16是1的整数倍,将c变量放在17的位置。
- 最后计算总的对齐值,min(max(int、double、char), 默认对齐值) = min(8, 8) = 8, 17不是8的正数倍,填充到24.
- 最终 sizeof(struct Tag1) 的值是 24.
嵌套的结构体的对齐
主要流程差不多,把内部的结构体当作一个变量,先计算这个变量的sizeof。
注意点
- 在一些网络通讯中,如果主机A发送一个结构体的数据到主机B,此时对数据流进行类型转换,可能会出现问题。解决办法是主机A、主机B都设置 # pragma pack(1) .