内存对齐是编译器强制执行的布局规则,非可选优化;成员偏移量必须为其对齐数(通常等于类型大小,受编译器默认限制)的整数倍,顺序影响填充,结构体总大小为最大对齐数的整数倍,末尾补齐确保数组元素对齐。

内存对齐不是可选的“优化技巧”,而是编译器默认强制执行的布局规则;不理解它,sizeof结果会出人意料,跨平台序列化可能读错字段,memcpy到未对齐缓冲区甚至触发硬件异常(尤其在ARM或RISC-V上)。
结构体成员偏移量怎么算?看对齐数和顺序
每个成员的起始地址必须是其「对齐数」的整数倍。对齐数通常等于该类型大小,但受编译器默认对齐限制(如MSVC默认#pragma pack(8),GCC默认无上限,取类型大小)。
-
char对齐数是1,可从任意地址开始 -
int(4字节)在64位Linux下对齐数是4,在x86-64 Windows下也是4;但double始终倾向8字节对齐 - 成员顺序直接影响填充:把
char、short堆在前面,int、double挤在后面,会导致大量中间填充;反过来排(大→小)能显著减少空洞 - 嵌套结构体的对齐数 = 其内部最大对齐成员的对齐数,它决定了该子结构在父结构中的起始偏移
为什么sizeof比成员总和大?补齐发生在结尾
结构体总大小必须是「自身对齐数」的整数倍——而自身对齐数等于所有成员(含嵌套结构)中最大的那个对齐数。
- 例如
struct { char a; int b; }:成员对齐数分别是1和4 → 结构体对齐数=4;实际布局是a占1字节 + 3字节填充 +b占4字节 → 总长8字节(4的倍数) - 如果末尾刚好凑满(如
struct { int a; short b; char c; },按顺序排是4+2+1=7,但需补1字节到8),补齐只加在末尾,不影响已有成员偏移 - 这个补齐不是“浪费”,而是保证数组中每个元素都满足对齐要求:若
sizeof(S) == 7,那么S arr[2]中arr[1]的起始地址就是&arr[0] + 7,不满足int成员的4字节对齐
未对齐访问在哪些场景会真正出问题?
不是所有平台都宽容。x86-64允许未对齐mov指令(性能略降),但ARMv7及更早版本、RISC-V默认禁止,直接触发alignment fault信号(SIGBUS)。
- 用
reinterpret_cast把char*强转为int*再解引用,且原地址不是4的倍数 → ARM上立即崩溃 - 网络包解析时,直接把收到的
uint8_t[]按结构体reinterpret_cast,若协议没考虑对齐,极易踩坑 -
std::vector分配的内存只保证1字节对齐,不能直接emplace_back一个double进去并期望它自然对齐 - 使用
aligned_alloc或_mm_malloc申请内存时,你指定的是起始地址对齐要求,但后续结构体成员仍要遵守各自对齐规则
最常被忽略的一点:对齐是编译期静态决策,offsetof返回的偏移、memcpy拷贝范围、甚至std::bit_cast的合法性,全部依赖这套规则。改一个成员类型或顺序,可能让整个二进制协议格式失效。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











