14.结构体对齐.md 3.7 KB

结构体对齐

判别规则(核心,唯一要记的)

  • ① 每个成员偏移 = 它自身对齐值的整数倍(char=1 / short=2 / int=4)
  • ② 结构体总大小 = 最大成员对齐值的整数倍
  • ③ 想省空间 → 成员从大到小排

一句话:"偏移必须能整除自己的对齐值,总大小必须能整除最大对齐值。"

抽象描述(一句话本质)

CPU 按"对齐值"访问内存最省事——4 字节对齐一次读 4 字节。编译器在成员间插填充字节,让每个成员偏移 = 自身对齐值整数倍;结构体总大小 = 最大对齐值整数倍。对齐是拿空间换速度,pack 是拿速度换空间

正反例(建立直觉)

  • 正例1struct A { char c; int i; char d; }; → c@0、i@4(补 3)、d@8,用满 9 → 补到 12。sizeof(A) = 12(不是 6)
  • 正例2(顺序影响大小)struct B { char c; char d; int i; }; → c@0、d@1 紧挨、i@4,sizeof(B) = 8。同样三个成员 A=12、B=8 → 从大到小排最省空间
  • 正例3offsetof(struct A, i) = 4,offsetof(struct A, d) = 8
  • 反例1:sizeof = 成员之和 → 错,有填充字节
  • 反例2:结构体数组每个元素也要对齐 → 总大小必须是最大对齐值整数倍(否则第二个元素错位)

易混对比

默认对齐 #pragma pack(1)
填充 有(按成员对齐值) (强制 1 字节对齐)
大小 更大(浪费但快) 最小(省空间但慢)
适用 普通内存结构 通信协议帧、Flash 存储

变体验证(3 题,全过=学会)

  1. struct { char c; int i; char d; }; → sizeof = 12(c@0、i@4、d@8,补到 12)
  2. struct { char a; char b; short s; }; → sizeof = 4(两个 char 挨着 0、1,s@2,共 4)
  3. 陷阱题:#pragma pack(1)struct { char c; int i; }; → sizeof = 5(无填充,1+4)

口述要点(面试怎么讲)

  • 结论先行:偏移 = 自身对齐值整数倍;总大小 = 最大对齐值整数倍;省空间从大到小排
  • 为什么要对齐:对齐后 CPU 一次读一个字的边界,最省时间、偏移计算方便;不对齐的 int 跨两个 4 字节边界 → CPU 读两次再拼接,慢;某些 ARM 配置下直接触发硬件异常
  • 协议帧为什么 pack(1):协议帧要和通信双方约定的字节布局严格一一对应。不 pack 时编译器插入的填充字节内容不确定,双方按不同布局解析就错位。pack(1) 保证结构体每个字节都有意义,才能把结构体指针直接当帧收发;代价是访问慢(同样适用于 Flash 存配置表)
  • 最省内存排序:成员按对齐值从大到小排(int → short → char),填充最少
  • 易错点:sizeof ≠ 成员之和;char 后紧跟 int 会补 3 字节;pack(1) 后无填充

关系网络

相邻概念 和本主题的关系 孤立理解会犯的错
union union 大小 = 最大成员(对齐值最大者),共用一块 以为 union 也像 struct 累加
位域 位域也是压缩内存的手段,但可移植性差 以为对齐和位域互斥
程序分段 对齐决定段内数据怎么摆 以为分段和段内布局无关
通信协议 pack(1) 保证结构体 = 协议帧 不用 pack 导致双方解析错位

学习日期

2026-08-03


每主题一页,复习时只翻本目录。