什么是数据对齐方式
结构体占用内存的字节数为什么有时候会和每个成员的字节数的加和不等?是因为对齐方式。
为了让处理器快速读写内存里面的数据,默认情况,编译器会把:
1 个字节的变量,例如 char 类型的变量,放在任意地址的位置上。
2 个字节的变量,例如 short 类型的变量,放在 2 的整数倍的地址上;
4 个字节的变量,例如 long / float 类型的变量,放在 4 的整数倍地址上;
8 个字节的变量,例如 long long / __int64 或 double 类型的变量,放在 8 的整数倍地址上;
16 个字节的变量,例如 __int128 类型的变量,放在 8 的整数倍地址上,因为默认的对齐方式是 8。
变量在内存里面的顺序,和定义变量的顺序相同。为了符合对齐方式,就会在变量之间加入填充字节,让后面的变量放在按照对齐方式的规则的地址上。
只有在定义结构体的时候,非常在意结构体占用内存的字节数的时候,才会发现由于对齐方式引起的每个成员的字节数加和不等于结构体的字节数。
例如,定义了 4 个变量,这 4 个变量在内存里面占用的字节数为 24 个字节,虽然他们的字节数加在一起只有 15 个字节。
char a;
double b;
short c;
float d; |
假定 a 放在地址为 0 的位置,a 之后的下一个地址是 1, 但是不能把 b 放在地址 1 上,因为 b 是 8 个字节的变量,所以往后找到最近的一个 8 的整数倍地址,即地址为 8 的位置上,那么 b 占用的地址空间为 8 ~ 15。变量 c 放在下一个地址 16 的位置上正好合适,c 是 2 个字节的,16 是 2 的整数倍,c 占用的地址空间为 16 ~ 17,变量 d 放在下一个地址 18 的位置不合适,d 是 4 个字节的变量,18 不是 4 的整数倍,所以往后放在了最近的地址 20 的位置上,d 占用的地址空间为 20 ~ 23。最终结果:从 a 到 d 整体占用的地址空间为 0 ~ 23,一共 24 个字节。
Copyright © Victor Chen, http://www.cppfans.com/
改变数据对齐方式的方法
#pragma pack(push) // 记住这个位置原先的对齐方式
#pragma pack(n) // 改变对齐方式为 n, n 的值为 1, 2, 4, 8, 16
#pragma pack(pop) // 恢复 push 记住的对齐方式
• n 的值表示的意义为超过 n 个字节的变量,放在 n 的整数倍地址上,少于 n 个字节的变量,放在变量字节数的整数倍地址上
• push 和 pop 是嵌套关系,内层的 push 和内层 pop 对应,外层的 push 和外层的 pop 对应。
• push 和 n 可以写在一起,用逗号隔开,例如:
#pragma pack(push,1)
相当于
#pragma pack(push)
#pragma pack(1)
n 值 |
对齐方式 |
1 |
每个变量都可以放在任意地址上 |
2 |
1 个字节的变量随意,2 个字节以上的变量放在 2 的整数倍地址 |
4 |
1 个字节的变量随意,2 个字节的变量放在 2 的整数倍地址,4 个字节以上的变量放在 4 的整数倍地址 |
8 |
1 个字节的变量随意,2 个字节的变量放在 2 的整数倍地址,4 个字节的变量放在 4 的整数倍地址,8 个字节以上的变量放在 8 的整数倍地址 |
16 |
1 个字节的变量随意,2 个字节的变量放在 2 的整数倍地址,4 个字节的变量放在 4 的整数倍地址,8 个字节的变量放在 8 的整数倍地址,16 个字节以上的变量放在 16 的整数倍地址 |
例1:默认的对齐方式
typedef struct
{
char a;
double b;
short c;
float d;
} C;
ShowMessage(sizeof(C)); |
运行结果:24
例2:设定对齐方式为 1
#pragma pack(push,1)
typedef struct
{
char a;
double b;
short c;
float d;
} D;
#pragma pack(pop)
ShowMessage(sizeof(D)); |
运行结果:15
|