首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

结构体与类内存字节对齐深度解析

     編輯:關於C語言

为什么要对齐

CPU 以字长(32 位机 4 字节,64 位机 8 字节)为单位访问内存。若变量地址未对齐,访问一个 int 可能需两次总线周期(跨越两个对齐块),降低性能。编译器通过在成员间插入填充字节,保证每个成员按其自然对齐要求放置,同时使结构体整体大小为最宽成员对齐要求的整数倍,从而数组连续存储时每个元素仍对齐。

核心规则

  1. 成员对齐:第 i 个成员偏移量 offset_i 必须是 min(alignof(type_i), #pragma pack 值) 的整数倍。
  2. 结构体整体对齐:sizeof(Struct) 必须是所有成员有效对齐值的最大值的整数倍(尾部填充)。
  3. 首地址对齐:结构体变量首地址由编译器/分配器保证满足最大对齐要求。

实例推演

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 控制

#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,布局更复杂,但对齐原则不变。

实用技巧

  1. 成员重排:按类型大小降序声明(double → int → short → char)可最小化填充。
  2. offsetof 宏:offsetof(Struct, member) 获取编译期偏移,验证布局。
  3. alignas (C++11):alignas(16) struct S { ... }; 强制更严格对齐(SIMD、缓存行对齐)。
  4. 调试:gcc -fdump-record-layouts、clang -cc1 -fdump-record-layouts 打印完整布局。

常见误区

  • “#pragma pack(1) 能省内存” ✓ 但可能导致非对齐访问崩溃(ARM、SPARC)或严重性能下降(x86 非对齐访问需微代码处理)。
  • “空结构体大小为 0” ✗ C++ 为 1,C (GNU 扩展) 为 0。
  • “sizeof 等于成员大小之和” ✗ 忽略填充与尾部对齐。

总结

字节对齐是空间换时间的经典权衡。理解自然对齐、填充字节、整体对齐三大原则,善用 #pragma pack、alignas、成员重排,即可在性能、兼容性、内存占用间游刃有余。调试时多看编译器生成的布局表,比死记规则可靠得多。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved