可避免的填充“unfull”结构

我试图给这个答案提供一个“逻辑”反例 – 表明根据结构大小对结构成员进行排序可以最小化填充,当我遇到似乎不合逻辑的时候。

想象一下以下结构:

struct A { int32_t a; int16_t b; }; 

这个结构的sizeof通常被填充到8个字节,以确保a例如在struct A的数组中对齐。

现在想象这些其他结构:

 struct B { struct A a, b; int16_t c, d; }; struct C { struct A a; int16_t c; struct A b; int16_t d; }; 

正如预期的那样,由于填充, struct B大小为20。 但是,我原本期望struct C大小为16,因为可以避免填充,但是ideone和gcc(有或没有优化)给出24字节的大小,显然在每个成员之后填充2个字节。

我的理由是struct A实际上只有6个字节,并且必要时应填充,例如在struct A的数组中或在struct B用法。 但是,在struct Cstruct A填充是不必要的,并且可以将c放置在a的填充可能已经存在的位置,并且与db相同。

为什么编译器不会通过将c放在c的填充位置来最小化填充?


PS我明白sizeof(struct A)必须返回8.否则memset(array_of_A, 0, N * sizeof *array_of_A)将无法正常工作,因为array_of_A将包含填充,而N * sizeof *array_of_A将忽略该填充。

我唯一能想到的可能是一个问题是,通过上面的优化, sizeof(struct C)小于其所有成员的sizeof 。 但是,我无法想到这样的事情可能成为问题的情况(即不基于未定义行为的用法)。

memcpy(&someC.a, &someA, sizeof(someC.a))将写入someC.c

这就是我试图得到的关于sizeof()必须与众不同的评论。 要使memcpy()工作, sizeof(someC.a)必须与sizeof(someA)不同,而这似乎要求很多麻烦而且很难找到bug。

 struct C someC; struct A someA; *(struct A*)&(someC.a) = someA; 

如果编译器支持您描述的填充,则上面的赋值可能会失败(错误地写入someC.c )。

编辑:上面的示例依赖于分配结构时的编译器行为。 正如我所知道的(一个刚刚检查过的) gcc副本,因为结构是一个平坦的内存区域,这不是成员方面的。

编辑:从“失败”更改为“可能失败”,因为未定义填充位是否应复制,请参见ISO_IEC_9899_2011第6.2.6.1节第6项:

当值存储在结构或联合类型的对象中(包括在成员对象中)时,对应于任何填充字节的对象表示的字节采用未指定的值.51)

和脚注51)

51)因此,例如,结构分配不需要复制任何填充比特。