一文彻底搞懂 C 结构体对齐:从原理到内存布局实战

2 阅读2分钟

C语言中的结构体对齐(Struct Alignment)是为了提高 CPU 访问内存的效率,编译器会按照一定的规则在结构体成员之间插入“填充字节(padding)”,使得每个成员的地址满足其对齐要求(alignment requirement)


一、基本概念

  • 对齐要求(Alignment Requirement) :通常等于该类型在当前平台上的大小(但不总是)。例如:

    • char:1 字节,对齐要求为 1
    • short:2 字节,对齐要求为 2
    • int:4 字节,对齐要求为 4
    • double:8 字节,对齐要求为 8(在某些 32 位系统上可能是 4)
    • 指针:通常是 4(32 位)或 8(64 位)
  • 结构体总大小:必须是其最大成员对齐要求的整数倍(即整体对齐)。


二、对齐规则(以默认对齐方式为例)

  1. 第一个成员从偏移 0 开始。
  2. 后续每个成员的起始偏移必须是其自身对齐要求的整数倍。如果当前偏移不满足,则在前面填充字节。
  3. 整个结构体的大小必须是结构体中最大对齐要求的整数倍。如有必要,在末尾填充。

三、示例分析

示例 1:

struct A {
    char a;     // 1 字节
    int b;      // 4 字节
    short c;    // 2 字节
};

分析:

  • a 从偏移 0 开始,占 1 字节 → 下一个可用偏移 = 1
  • b 要求 4 字节对齐 → 下一个 4 的倍数是 4 → 在 1~3 插入 3 字节 padding
  • b 占 4 字节(偏移 4~7)→ 下一个可用偏移 = 8
  • c 要求 2 字节对齐 → 8 是 2 的倍数 → 直接放(偏移 8~9)
  • 当前大小 = 10 字节
  • 最大对齐 = max(1,4,2) = 4
  • 总大小必须是 4 的倍数 → 向上对齐到 12

sizeof(struct A) = 12


示例 2(优化顺序减少 padding):

struct B {
    int b;      // 4
    short c;    // 2
    char a;     // 1
};
  • b:偏移 0~3
  • c:偏移 4~5(4 是 2 的倍数)
  • a:偏移 6(1 对齐任意地址)
  • 当前大小 = 7
  • 最大对齐 = 4 → 总大小需为 4 的倍数 → 补到 8

sizeof(struct B) = 8(更节省空间!)


四、如何查看/控制对齐?

1. 查看对齐值(C11 起支持):

#include <stdalign.h>
printf("%zu\n", alignof(int)); // 输出 int 的对齐要求

2. 使用编译器指令控制对齐:

  • GCC / Clang

    struct __attribute__((packed)) S { ... }; // 禁用对齐(可能影响性能)
    struct __attribute__((aligned(8))) S { ... }; // 强制按 8 字节对齐
    
  • MSVC

    #pragma pack(push, 1)  // 1 字节对齐
    struct S { ... };
    #pragma pack(pop)
    

⚠️ 注意:强制取消对齐(如 packed)可能导致 CPU 访问未对齐数据而变慢,甚至在某些架构(如 ARM)上引发异常。


五、总结口诀

“各就各位,对齐先行;整体补齐,最大为尊。”