内存对齐与变量/结构体分析
在 C++ 中,内存对齐(Memory Alignment) 是编译器在为变量或结构体分配内存时,为了提高 CPU 访问效率 而进行的一种内存布局策略。
它的核心思想是:数据的起始地址要满足一定的对齐要求,而不是紧密地一个接一个地存放。
1️⃣ 为什么需要内存对齐
-
硬件原因
许多 CPU 在访问某些数据类型时,要求数据的地址是特定倍数,否则访问效率会下降,甚至直接抛出硬件异常。
例如:32 位 CPU 通常要求 4 字节的int
变量地址是 4 的倍数。 -
性能原因
CPU 以字(word)为单位读取内存(一次通常读取 2/4/8 字节)。
如果数据没有按字对齐,CPU 可能需要两次总线访问才能取到完整数据,降低效率。
2️⃣ 对齐规则(常见情况)
编译器通常遵循以下规则(不同平台/编译器可能略有差异):
类型 | 对齐值(alignment) |
---|---|
char | 1 字节 |
short | 2 字节 |
int / float | 4 字节 |
double | 8 字节(在 64 位系统上通常是 8) |
👉 对齐值 = min(类型自身大小, 编译器默认对齐值)
-
例如:在默认对齐值为 8 的 64 位系统上:
int
的大小 4,取 min(4, 8) → 对齐值 4 , 这意味着每个 int 的 起始地址必须是 4 的倍数。double
的大小 8,取 min(8, 8) → 对齐值 8
3️⃣ 结构体的对齐
结构体(struct/class)对齐比单一变量更复杂,需要遵循 成员对齐 + 整体对齐:
规则
- 每个成员 的地址必须是其自身对齐值的整数倍。
- 结构体总大小 必须是其 最大成员对齐值 的整数倍(必要时在末尾填充 padding)。
例子 1
struct A {char c; // 1 字节int i; // 4 字节
};
内存布局:
| c | pad(3) | i i i i |
char
c 占 1 字节,接着要对齐到 4 字节边界 → 填充 3 字节。- 总大小 = 8 字节,而不是 5。
例子 2:调整顺序减少填充
struct B {int i;char c;
};
布局:
| i i i i | c | pad(3) |
- 总大小还是 8(因为整体必须是最大成员对齐值 4 的倍数)。
- 但如果有多个成员,合理排序可以减少浪费。
4️⃣ 控制对齐方式
编译器提供手段来改变默认对齐:
-
#pragma pack(n)
- 让结构体按
n
字节对齐。
#pragma pack(1) struct C {char c;int i; }; #pragma pack()
结果大小 = 5 字节(几乎无 padding),但可能降低访问效率。
- 让结构体按
-
alignas
(C++11)- 指定特定变量或结构体的对齐要求。
struct alignas(16) D {double d; };
强制
D
的对齐为 16 字节。 -
alignof
(C++11)- 查询类型的对齐值。
std::cout << alignof(double) << std::endl;
5️⃣ 小结
概念 | 说明 |
---|---|
内存对齐 | 数据地址按特定字节边界存放 |
原因 | 提高 CPU 访问速度,避免硬件异常 |
结构体大小 | 成员最大对齐值的倍数 |
优化 | 成员排序、#pragma pack 、alignas |
一句话记忆:
对齐是以空间换时间:多花几个字节的填充 (padding),换来 CPU 读写的高效和安全。