Note
什么是内存对齐(字节对齐)?
说明对齐的目的、自然对齐规则,以及它对结构体大小、性能和原子访问的影响。
省流
内存对齐指:某些类型的数据在内存中的起始地址必须是其对齐要求的整数倍。例如 4 字节的
int通常要求起始地址能被 4 整除。编译器为结构体插入填充(padding),并可能在结构体末尾补齐,使数组中下一个元素仍然对齐。对齐首先来自硬件与 ABI 约定:不少 CPU 对未对齐访问更慢,有的指令集或原子操作要求必须对齐,否则可能陷入异常或失去原子性。对齐也能让 CPU 一次总线/缓存行访问取到完整对象,减少跨边界访问。
struct、class(C++)、union都会受对齐规则约束。union的大小至少能容纳最大成员,并满足所有成员的对齐要求;结构体则按成员声明顺序布局并插入填充。具体对齐大小由平台 ABI 与编译选项决定,32 位与 64 位、不同架构之间可能不同。
为什么要对齐?
- 硬件访问效率:对齐后的读写作一次对齐内存访问完成的概率更高。
- 指令与原子语义:部分加载/存储、锁自由原子操作要求自然对齐。
- ABI 稳定:同一平台上,编译器按统一规则排布局,才能让不同编译单元正确链接与传参。
常见规则(面试常用模型)
以“自然对齐”为默认讲解模型:
- 成员对齐值通常取其类型大小与平台最大对齐中的约定值(例如 32 位上
int对齐 4)。 - 结构体自身的对齐,取成员中最大对齐要求。
- 结构体总大小必须是自身对齐的整数倍,便于该结构体组成数组时每个元素仍对齐。
地址: 0 1 2 3 4 5 6 7 8 ...
[a][pad pad pad][ int b ][ ...
上面的 pad 就是编译器插入的填充字节,对业务逻辑不可见,但计入 sizeof。
和“浪费内存”的关系
填充会让 sizeof(struct) 大于各字段大小之和。这是用空间换取符合 ABI 与硬件约束的布局。若需紧凑存储,可以使用:
- 调整成员声明顺序,减少中间空洞
- 编译器提供的紧凑打包属性(如 GNU 的
__attribute__((packed)))
打包能减小体积,但可能带来未对齐访问代价,或在部分场景下不安全;默认布局通常更稳妥。
答题时建议主动补一句
对齐规则以目标平台 ABI 为准;口头计算结构体大小时,要先声明假设(例如“按常见 32 位 ILP32,char 对齐 1、int 对齐 4”),再逐步累加偏移与末尾填充。