为什么结构的大小应该反映它的对齐?

时间:2015-08-19 15:40:04

标签: c size padding memory-alignment

根据Wikipedia

  

最后一个成员填充所需的字节数,以便结构的总大小应该是任何结构成员的最大对齐的倍数

根据我的理解,这意味着以下内容:

struct A {
  char *p;     // 8 bytes
  char c;      // 1 byte
};
struct B {
  struct A a;  // 16 bytes
  char d;      // 1 bytes
};

struct A的大小为16个字节,struct B的大小为24个字节。

常见的解释是A的数组应该可以在数组的地址访问它们的元素,加上索引的大小为A

但我不明白为什么会这样。为什么我们不能说A的大小为9而B的大小为10(两个字节对齐),并且在索引到数组时使用特殊的“数组存储”大小?

当然,我们仍然以与其对齐兼容的方式将这些类型存储在数组中(使用16个字节来存储每个B元素)。然后,我们只是通过考虑它们的对齐来计算元素地址,而不是单独考虑它们的大小(编译器可以静态地执行)。

例如,我们可以在B的1Kb字节数组中存储64个对象,而不是仅存储42个。

1 个答案:

答案 0 :(得分:1)

在C的每个翻译单元中,无论sizeof(T)的上下文如何,T都是相同的。您的提案会为sizeof(T)引入至少两个值:一个用于T的数组,另一个用于T的单个对象。这基本上将上下文依赖引入sizeof运算符。它与C处理指针,数组和对象地址的方式不兼容。

请考虑以下事项:

void zero_A(struct A *a) { memset(a,0,sizeof(*a)); }

/* ... */
struct A single;
struct A several[3];
struct B b;

b.d = 3;
zero_A(&b.a);
zero_A(&single);
zero_A(several+1);

根据您的提案,zero_A必须知道传递的指针是否指向数组上下文中的struct Asizeof(*a) == 16}或struct A之外的数组上下文(其中sizeof(*a) == 9)。标准C不支持此功能。如果编译器猜错了,或者信息丢失了(例如:在volatile struct A *的往返中),那么zero_A(&single)将调用未定义的行为(通过写single的边界),zero_A(&b.a)将覆盖b.d并调用未定义的行为。

将结构紧密地打包到数组中是一个相对不常见的要求,并且向sizeof添加上下文依赖会给语言,库和ABI带来许多复杂性。有时您需要这样做,C为您提供所需的工具:memcpy和工会。