1. 内存对齐的基本原理
1.1. 什么是内存对齐
内存对齐指数据在内存中存放时,其起始地址必须是某个值的整数倍,该值被称为对齐模数(alignment modulus)。
1.2. 为什么需要内存对齐
- 硬件要求:部分CPU仅能从对齐的地址读取数据。
- 性能优化:对齐的数据访问速度更快。
- 平台兼容性:不同平台可能存在不同的对齐要求。
1.3. 对齐规则
1 2 3 4 5 6
| struct Example { char a; int b; double c; };
|
2. 内存对齐的详细规则
(64位操作系统)
2.1. 基本类型对齐要求
1 2 3 4 5 6 7 8 9
| char short int float double long long long 指针
|
2.2. 结构体对齐规则
1 2 3 4 5 6 7 8 9 10 11
| struct S1 { char c; int i; };
struct S2 { int i; char c; };
|
2.3. 联合体对齐
1 2 3 4 5
| union U { int i; double d; char c; };
|
3. 控制内存对齐的方法
3.1. pack预处理指令
pack预处理指令有两种用法:
- 方式一:
#pragma pack(push, N) / #pragma pack(pop)
- 方式二:
#pragma pack(N) / #pragma pack()
3.1.1. #pragma pack(push, N) / #pragma pack(pop)
基本用法:
1 2 3 4 5 6 7
| #pragma pack(push, 1) struct TightPacked { char a; int b; short c; }; #pragma pack(pop)
|
复杂用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| #include <iostream> #include <cstddef>
#pragma pack(push, 8) struct StructA { char a; double b; int c; };
#pragma pack(push, 4) struct StructB { char a; double b; int c; };
#pragma pack(push, 1) struct StructC { char a; double b; int c; };
#pragma pack(pop) struct StructD { char a; double b; int c; };
#pragma pack(pop) struct StructE { char a; double b; int c; };
#pragma pack(pop)
int main() { std::cout << "Size of StructA (pack 8): " << sizeof(StructA) << std::endl; std::cout << "Size of StructB (pack 4): " << sizeof(StructB) << std::endl; std::cout << "Size of StructC (pack 1): " << sizeof(StructC) << std::endl; std::cout << "Size of StructD (back to 4): " << sizeof(StructD) << std::endl; std::cout << "Size of StructE (back to 8): " << sizeof(StructE) << std::endl; return 0; }
|
运行结果:
1 2 3 4 5
| Size of StructA (pack 8): 24 Size of StructB (pack 4): 16 Size of StructC (pack 1): 13 Size of StructD (back to 4): 16 Size of StructE (back to 8): 24
|
3.1.2. #pragma pack(N) / #pragma pack()
基本用法:
1 2 3 4 5 6 7
| #pragma pack(1) struct TightPacked { char a; int b; short c; }; #pragma pack()
|
复杂用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <cstddef> #include <iostream>
#pragma pack(8) struct StructA { char a; double b; int c; };
#pragma pack(4) struct StructB { char a; double b; int c; };
#pragma pack(1) struct StructC { char a; double b; int c; };
#pragma pack() struct StructD { char a; double b; int c; };
#pragma pack() struct StructE { char a; double b; int c; };
#pragma pack()
int main() { std::cout << "Size of StructA (pack 8): " << sizeof(StructA) << std::endl; std::cout << "Size of StructB (pack 4): " << sizeof(StructB) << std::endl; std::cout << "Size of StructC (pack 1): " << sizeof(StructC) << std::endl; std::cout << "Size of StructD (back to 4): " << sizeof(StructD) << std::endl; std::cout << "Size of StructE (back to 8): " << sizeof(StructE) << std::endl;
return 0; }
|
运行结果:
1 2 3 4 5
| Size of StructA (pack 8): 24 Size of StructB (pack 4): 16 Size of StructC (pack 1): 13 Size of StructD (back to 4): 24 Size of StructE (back to 8): 24
|
3.1.3. 总结概述
功能:
两组指令都实现相同的目标。
- 将结构体/类成员的对齐方式设置为N字节
- 然后恢复到之前的对齐设置
区别:
- 在处理简单单层的对齐设置时,表现几乎相同。
- 在处理多层嵌套的对齐设置时,方式一可以完美支持,某些编译器下方式二可能无法正确恢复嵌套的对齐设置。
- 建议:尽量使用方式一(
push/pop),更安全、更清晰。
注意事项:
- pop必须与push配对,否则会导致编译错误或意外行为
- 跨文件边界要小心,确保在同一个编译单元内完成push/pop对
3.2. __attribute__预处理指令
3.2.1. __attribute__((packed))
__attribute__((packed))是GCC/Clang编译器的扩展属性,用于取消结构体的内存对齐,使其成员按最小可能对齐方式(1字节对齐)紧密排列。
基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| struct __attribute__((packed)) PackedStruct { char a; int b; double c; };
struct PackedStruct { char a; int b; double c; } __attribute__((packed));
typedef struct __attribute__((packed)) { char a; int b; } PackedType;
struct MyStruct { char a; int b; } my_var __attribute__((packed));
|
3.2.2. __attribute__((aligned(N)))
__attribute__((aligned(N))) 是GCC/Clang编译器的扩展属性,用于指定变量、类型或成员的对齐方式,强制要求按指定的字节数对齐。如:aligned(16)表示按16字节边界对齐。
基本语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| int __attribute__((aligned(16))) aligned_var;
double __attribute__((aligned(32))) matrix[1024];
struct __attribute__((aligned(16))) AlignedStruct { int a; char b; };
struct AnotherStruct { int x; double y; } __attribute__((aligned(32)));
struct MemberAligned { char a; int b __attribute__((aligned(16))); double c; };
struct __attribute__((packed, aligned(16))) Mixed { char a; int b; };
|
3.3. C11/C++11的alignas用法
TODO
4. 应用场景
- 文件格式处理
- 网络数据传输
- 嵌入式系统特殊硬件要求等