CPU 以字长(32 位机 4 字节,64 位机 8 字节)为单位访问内存。若变量地址未对齐,访问一个 int 可能需两次总线周期(跨越两个对齐块),降低性能。编译器通过在成员间插入填充字节,保证每个成员按其自然对齐要求放置,同时使结构体整体大小为最宽成员对齐要求的整数倍,从而数组连续存储时每个元素仍对齐。
i 个成员偏移量 offset_i 必须是 min(alignof(type_i), #pragma pack 值) 的整数倍。sizeof(Struct) 必须是所有成员有效对齐值的最大值的整数倍(尾部填充)。struct A { char c; int d; short e; };
// 假设 4 字节对齐环境
// c @0 (1B) → 填充 3B → d @4 (4B) → e @8 (2B) → 尾部填充 2B → sizeof=12
struct B { char c; long long d; int e; short f; A g; char h; int i; };
// 8 字节对齐环境
// c @0 → 填充 7B → d @8 (8B) → e @16 (4B) → f @20 (2B) → 填充 2B → g @24 (sizeof=12, align=4) → h @36 → 填充 3B → i @40 (4B) → 尾部填充 8B → sizeof=48
#pragma pack(push, 1) // 按 1 字节对齐(紧凑)
struct Packed { char c; int d; };
#pragma pack(pop) // 恢复默认
// sizeof(Packed) = 5,但访问 d 可能产生非对齐访问异常(某些架构)或性能损耗。
常用于网络协议头、文件格式定义、共享内存等必须精确控制布局的场景。
vptr 通常放在对象最开始(或末尾),占指针大小,按指针对齐。sizeof=1(保证地址唯一),作为基类子对象时可空基类优化 (EBO) 占 0 字节。vptr/vbptr,布局更复杂,但对齐原则不变。double → int → short → char)可最小化填充。offsetof(Struct, member) 获取编译期偏移,验证布局。alignas(16) struct S { ... }; 强制更严格对齐(SIMD、缓存行对齐)。gcc -fdump-record-layouts、clang -cc1 -fdump-record-layouts 打印完整布局。#pragma pack(1) 能省内存” ✓ 但可能导致非对齐访问崩溃(ARM、SPARC)或严重性能下降(x86 非对齐访问需微代码处理)。sizeof 等于成员大小之和” ✗ 忽略填充与尾部对齐。字节对齐是空间换时间的经典权衡。理解自然对齐、填充字节、整体对齐三大原则,善用 #pragma pack、alignas、成员重排,即可在性能、兼容性、内存占用间游刃有余。调试时多看编译器生成的布局表,比死记规则可靠得多。