结构填充

假设这个 struct 是用 32 位编译器定义和编译的:

struct test_32 {
    int a;      // 4 byte
    short b;    // 2 byte
    int c;      // 4 byte    
} str_32;

我们可能期望这个 struct 只占用 10 个字节的内存,但是通过打印 sizeof(str_32),我们看到它使用了 12 个字节。

发生这种情况是因为编译器将变量对齐以便快速访问。一种常见的模式是,当基类型占用 N 个字节时(其中 N 是 2 的幂,如 1,2,4,8,16 - 并且很少更大),变量应该在 N 字节边界上对齐( N 个字节的倍数)。

对于 sizeof(int) == 4sizeof(short) == 2 所示的结构,常见的布局是:

  • int a; 存储在偏移量 0; 4 号。
  • short b; 存储在偏移量 4; 2 号。
  • 偏移量为 6 的未命名填充; 2 号。
  • int c; 存储在偏移量 8; 4 号。

因此 struct test_32 占用 12 个字节的内存。在此示例中,没有尾随填充。

编译器将确保从 4 字节边界开始存储任何 struct test_32 变量,以便结构中的成员正确对齐以便快速访问。需要内存分配函数,如 malloc()calloc()realloc(),以确保返回的指针与任何数据类型的使用足够对齐,因此动态分配的结构也将正确对齐。

最终可能出现奇怪的情况,例如 64 位 Intel x86_64 处理器(例如 Intel Core i7 - 运行 macOS Sierra 或 Mac OS X 的 Mac),在 32 位模式下进行编译时,编译器将 double 对齐 4 字节边界; 但是,在相同的硬件上,当在 64 位模式下编译时,编译器将 double 对齐在 8 字节边界上。