1 内存对齐的基本概念
1.1 什么是内存对齐
内存对齐的概念:
- 不同类型的数据在内存中按照一定的规则排列。
- 而不一定是顺序的一个接一个的排列。
上面中Test1和Test2所占的内存空间在编译器的默认4字节的对齐方式下是不一样的,Test1占用12字节,Test2占用8字节。其内存图如下:
1.2 内存对齐的原因
为什么需要内存对齐?
- CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1、2、4、8、16…字节。
- 当读取操作的数据未对齐,则需要两次总线周期来访问内存,因此性能会大打折扣。
- 某些硬件平台只能从规定的相对地址处读取特定类型的数据,否则产生数据异常。
#pragma pack用于指定内存对齐方式。
2 #pragma pack
2.1 #pragma pack的使用方式
#pragma pack能够改变编译器的默认对齐方式。
以struct为例,分析内存对齐方式,struct占用的内存大小:
- 第一个成员起始于0偏移处。
- 每个成员按其类型大小和pack参数中较小的一个进行对齐。
- 偏移地址必须能被对齐参数整除。
- 结构体成员的大小取其内部长度最大的数据成员作为其大小。
- 结构体总长度必须为所有对齐参数的整数倍。
编译器默认情况下按照4字节对齐,gcc不支持8字节对齐。
结构体内存对齐分析(32位编译环境):
// 1字节对齐
#pragma pack(1)
struct Test0
{ // 对齐参数 起始字节 占用字节
char c1; // 1 0 1
short s; // 1 1 2
char c2; // 1 3 1
int i; // 1 4 4
}; // 总共占用8字节
#pragma pack()
#pragma pack(2)
struct Test1
{ // 对齐参数 起始字节 占用字节
char c1; // 1 0 1
short s; // 2 2 2
char c2; // 1 4 1
int i; // 2 6 4
}; // 总共占用10字节
#pragma pack()
#pragma pack(4)
struct Test2
{ // 对齐参数 起始字节 占用字节
char c1; // 1 0 1
char c2; // 1 1 1
short s; // 2 2 2
int i; // 4 4 4
}; // 总共占用8字节
#pragma pack()
#pragma pack(8)
struct S1
{ // 对齐参数 起始字节 占用字节
short a; // 2 0 2
long b; // 4 4 4
}; // 总共占用8字节
struct S2
{ // 对齐参数 起始字节 占用字节
char c; // 1 0 1
struct S1 d; // 4 4 8
double e; // 8 16 8
}; // 总共占用24字节
#pragma pack()
参考资料: