C 可避免的填充物,包括;“不完整”;结构

C 可避免的填充物,包括;“不完整”;结构,c,C,我试图给出一个“逻辑”反例,表明根据结构成员的大小对其进行排序可以最大限度地减少填充,但在我看来这是不合逻辑的 想象一下下面的结构: struct A { int32_t a; int16_t b; }; sizeof此结构通常填充为8字节,以确保a对齐,例如在结构a的数组中 现在想象一下其他结构: struct B { struct A a, b; int16_t c, d; }; struct C { struct A a; int16_t

我试图给出一个“逻辑”反例,表明根据结构成员的大小对其进行排序可以最大限度地减少填充,但在我看来这是不合逻辑的

想象一下下面的结构:

struct A
{
    int32_t a;
    int16_t b;
};
sizeof
此结构通常填充为8字节,以确保
a
对齐,例如在
结构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;
};
正如预期的那样,由于填充,结构B的大小为20。但是,我本来希望结构C的大小为16,因为可以避免填充,但是和gcc(有优化或没有优化)的大小为24字节,显然在每个成员之后填充2字节

我的推理是,
struct A
实际上只有6个字节,必要时应该填充,例如在
struct A
数组中或在
struct B
中使用。但是,在
struct C
中,不需要对
struct A
进行填充,并且
C
可以放置在
A
的填充位置,与
d
b
相同

为什么编译器不把
c
放在
a
的填充位置来最小化填充


另外,我知道
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
。然而,我想不出这样的事情会成为一个问题的情况(即,不是基于未定义行为的用法)

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

编辑:上面的示例依赖于编译器在指定结构时的行为。正如我所知道的(一个刚刚检查过的)
gcc
复制,因为结构是一个平坦的内存区域,它不是面向成员的

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

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

和脚注
51)

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

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

这就是我对
sizeof()
的评论想要表达的意思 要与众不同。要使
memcpy()
起作用,必须使用
sizeof(someC.a)
sizeof(someA)
不同,后者似乎要求很多
麻烦而且很难找到bug。

我不确定我是否理解这个问题,但我认为您所问的问题需要sizeof()在同一类型上返回不同的值。例如,sizeof(foo.A)与sizeof(struct A)不同。所以复制这些东西真的很难。为什么不重新排列成员呢?因为C编译器一开始并不是设计用来这样做的。它是按照你申报的顺序排列的。C是一种相当低级的语言,它让我们对许多事情有更多的控制,如果编译器改变了一些事情,那就不好了。如果我声明了成员
A
B
C
,我希望它们会按照这个顺序排列在内存中。@CharlieBurns,我的最后一段是试图解决这个问题
sizeof(foo.a)
不需要与
sizeof(struct a)
不同。当然,如果
sizeof(foo.a)
返回8,那么它的填充中包含
foo.c
,但是您不能访问该部分,因为它在结构外部,访问它将是未定义的行为。那么,如果
sizeof(foo.a)
返回8,会出现什么问题呢?@JeffMercado,我不是要求编译器对成员重新排序。该标准明确禁止(C11,6.7.2.1.6)不将memcpy(&someC.a,&someA,sizeof(someC.a))写入someC.c?我想这和答案是一样的,我的评论就是这个意思。sizeof(someC.a)==sizeof(someA)这是个陷阱,对吧。它指出,标准可能最终排除了填充,并定义不应复制该填充。但我想这可能会引入一个不必要的限制。我接受另一个答案,因为它给出了一个无条件破坏的例子。
struct C someC;
struct A someA;
*(struct A*)&(someC.a) = someA;