在某些计算机体系结构中,由于对齐限制,访问内存中值的指令将只接受所有地址的子集。例如,一条将 32 位值从内存复制到寄存器的指令可能要求该值位于可被 4 整除的地址。(您可能仍然能够逐字节获取该值,但这会慢得多,因为它需要多条指令)。如果值正确对齐,其他架构可能只会表现更好。而在其他架构中,它可能根本不重要。
因此,C 标准允许在结构中使用特定于实现的填充。通过添加填充,编译器可以确保每个成员都正确对齐(因为它可以强制结构本身对齐)。这允许我们声明以下内容并让编译器计算出确切的大小和偏移量:
struct A {
int x;
short y;
double z;
};
让我们看看编译器可能做什么。
假设您的系统为 short 值使用 2 个字节,int 值使用 4 个字节,double 值使用 8 个字节。假设大小为 N 的值需要放置在可被 N 整除的地址。
struct A {
int x; // 4 bytes, address must be divisible by 4.
double z; // 8 bytes, address must be divisible by 8.
short y; // 2 bytes, address must be divisible by 2.
};
如果我们只是将成员首尾相连,z 将在偏移量 4 处找到,该偏移量不能被 8 整除,因此计算机将无法有效访问该字段。因此编译器可能会使用填充。
struct A {
int x; // 4 bytes, address must be divisible by 4. // At offset 0.
// 4 bytes of padding. // At offset 4.
double z; // 8 bytes, address must be divisible by 8. // At offset 8.
short y; // 2 bytes, address must be divisible by 2. // At offset 16.
};
现在,z 位于偏移量 8 处,可被 8 整除。
但这还不够。
对齐限制施加在成员的绝对地址上,而不仅仅是它们的偏移量。因此,struct C 的成员只有在结构本身的地址位于可被 8 整除的地址处时才会正确对齐。编译器可以在您这样做时进行处理
struct A a;
如果你这样做了怎么办
struct A *array = malloc(sizeof(struct A) * n);
malloc 将返回一个满足所有可能对齐限制的指针,因此array[0] 将正确对齐,但是array[1] 呢?为了正确对齐,sizeof(struct A) 需要是 8 的倍数!所以会在末尾添加 padding 使结构体的大小成为 8 的倍数,我们最终得到这样的结果:
// Address must be divisible by 8, so sizeof(struct A) must be divisible by 8.
struct A {
int x; // 4 bytes, address must be divisible by 4. // At offset 0.
// 4 bytes of padding. // At offset 4.
double z; // 8 bytes, address must be divisible by 8. // At offset 8.
short y; // 2 bytes, address must be divisible by 2. // At offset 16.
// 2 bytes of padding. // At offset 18.
};
最后,您询问了struct C。应用上述,我们得到:
// Address must be divisible by 8, so sizeof(struct C) must be divisible by 8.
struct C {
double z; // 8 bytes, address must be divisible by 8. // At offset 0.
short y; // 2 bytes, address must be divisible by 2. // At offset 8.
// 2 bytes of padding. // At offset 10.
int x; // 4 bytes, address must be divisible by 4. // At offset 12.
// 0 bytes of padding. // At offset 16.
};