看到一篇讲解C/C++字节对齐不错的文章:
来源:链接~
字节对齐的原因:
为了提高 CPU 的存储速度,编译器会对 struct 的存储进行优化,即进行字节对齐。
对齐方式:
它们的字节对齐标准就是它的所有成员中字节数最大的数据的字节数。
一般在32为操作系统的情况下C/C++的变量所占用的字节数:
char: 1字节;
short: 2字节;
int: 4字节;
long: 4字节;
long long: 8字节;
float: 4字节;
double: 8字节;
bool: 1字节;
struct 中字节对齐需要满足的条件:
1、某个变量存放的起始位置相对于结构的起始位置的偏移量是该变量字节数的整数倍;
2、结构所占用的总字节数是结构种字节数最长的变量的字节数的整数倍。
例如:
struct Struct
{
double d1;
char d2;
int d3;
}a;
用VS查看结果:
上图左边的0,8,12,代表当前变量距离本类初始地址的距离。上图填充值为3
sizeof(a) = 8 + 1 + 3 + 4 = 16。其中补上的 3 个字节是为了让 int 型数据的起始位置相对于结构起始位置的偏移量为 4 的整数倍。
struct Struct
{
char d1;
double d2;
int d3;
}b;
VS查看内存输出结果:
上图中,填充7是为了分配d2的时候满足前面已经分配的内存大小是d2大小的倍数,最后填充的4个字节是为了满足总的字节数是结构中最大字节数的倍数。
sizeof(b) = 1 + 7 + 8 + 4 + 4= 24。 20 / 8 = 2 …… 4,所以需要再补上 4 个字节,使之成为 8 的 整数倍。
字节对齐的另一种方式:(PS:sizeof 属于关键字)
VC提供了 #pragma pack(n) 用来自定义字节对齐方式,其中n必须是2的幂次方。
有一下两种情况:
1、n 大于变量的字节数:偏移量只满足默认的字节对齐方式;
2、n 小于变量所占的字节数:偏移量默认,总字节长度是n的整数倍。
例如:
#pragma pack(push) // 保持对齐状态
#pragma pack(4) // 设定为 4 字节对齐
struct test
{
double m2;
int m3;
char m1;
};
#pragma pack(pop) // 恢复对齐状态
VS查看类的内存布局:
从上图可以清晰的看到:sizeof(test) = 8 + 4 + 1 + 3 = 16,其中最后的 3 是填充字段。
#pragma pack(push) // 保持对齐状态
#pragma pack(16) // 设定为 4 字节对齐
struct test
{
double m2;
int m3;
char m1;
};
#pragma pack(pop) // 恢复对齐状态
VS查看类的内存布局:
大小和上面一样。
再举个栗子:
#include <iostream>
using namespace std;
typedef struct A
{
char aChar;
int aInt_2;
short aInt;
}TypeA;
typedef struct B
{
char bChar[3];
TypeA bA;
double bDouble;
}TypeB;
int main()
{
TypeA a;
TypeB b;
cout << sizeof(a) << endl << sizeof(b) << endl;
return 0;
}
类A的内存布局图:
类B的内存布局图:
输出为 12 24
解析:
由上述对字节对齐问题的讨论很容易便可以得出此题的答案。
sizeof(TypeA) = 1 + 3 + 4 + 2 = 10。 10 / 4 = 2 …… 2,故需要再补上 2 个字节,即 sizeof(TypeA) = 12;
sizeof(TypeB) = 3 + 1 + 12 + 8 = 24。之所以补上 1 个字节是因为 TypeA 类型在 struct TypeB 中的默认对齐方式
是 4 个字节(即 int 的字节大小,也即 struct TypeA 中最长的字节)。
看最后一个例子:
typedef struct A
{
char aChar;
int aInt_2;
short aInt;
}TypeA;
typedef struct B
{
TypeA bA;
double bDouble;
}TypeB;
类B的内存布局:
可进一步看一下C++中计算类的大小:C++类的大小计算