1. 内存对齐:从现象到本质
1.1 那个令人困惑的8字节
第一次看到下面这个结构体时,我和大多数初学者一样感到困惑:
c复制struct stu1 {
char a; // 1字节
int b; // 4字节
}; // 实际大小:8字节
按照简单的加法计算,这个结构体应该是5字节(1+4),但实际测试结果却是8字节。这种"浪费"现象背后隐藏着计算机体系结构的重要设计原则。
1.2 CPU的内存访问机制
现代CPU访问内存时,并不是逐字节读取的。以常见的64位系统为例:
- 数据总线宽度为64位(8字节)
- 每次内存读取操作固定获取8字节数据
- 读取地址必须是8的倍数(对齐访问)
如果int变量b的地址不是4的倍数(比如0x0003),那么读取这个int需要:
- 从0x0000读取8字节(获取0x0003处的部分数据)
- 从0x0008读取8字节(获取剩余部分数据)
- 拼接两次读取的结果
这种非对齐访问会导致性能显著下降,在某些架构(如ARM)上甚至会引发硬件异常。
1.3 对齐的数学本质
内存对齐可以用模运算来描述。对于类型T的变量:
- 地址address必须满足:address % sizeof(T) == 0
- 结构体整体大小必须满足:sizeof(struct) % max_alignment == 0
其中max_alignment是结构体中所有成员的最大对齐数。这就是为什么stu1的大小是8而不是5——5不是4(int的对齐数)的整数倍。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 对齐规则深度解析
2.1 四大黄金法则
- 首成员规则:第一个成员始终位于偏移量0处
- 成员对齐规则:后续成员必须放在min(默认对齐数, 成员大小)的整数倍偏移量
- 结构体收尾规则:最终大小必须是最大成员对齐数的整数倍
- 嵌套结构体规则:嵌套的结构体对齐到其内部最大对齐数的整数倍
2.2 编译器默认对齐数
不同平台和编译器的默认对齐数不同:
- Visual Studio:8字节
- GCC/Linux:通常4字节(可通过
__attribute__((aligned(n)))修改) - 嵌入式系统:可能更小(如2字节)
查看默认对齐数的方法:
c复制#pragma pack(show) // VS专用
printf("默认对齐数:%zu\n", _Alignof(max_align_t));
2.3 实际案例分析
案例1:基础结构体
c复制struct example1 {
char a; // 0
int b; // 4-7
double c;
