避免“未填满”结构体中的可避免填充问题。

3
我试图给这个答案提供一个“逻辑上”的反例,表明根据结构体成员的大小对其进行排序将最小化填充时遇到了我认为不合逻辑的情况。

想象一下以下结构:

struct A
{
    int32_t a;
    int16_t b;
};

sizeof这个结构体通常会填充为8个字节,以确保例如在struct 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;
};

如预期,由于填充,struct B 的大小为20。然而,我本来希望struct C的大小为16,因为可以避免填充,但是ideone和gcc(有或没有优化)都给出了24字节的大小,显然在每个成员后面填充了2个字节。
我的理解是,struct A实际上只有6个字节,应该在必要时进行填充,例如在struct B数组中或在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。但是,我想不出会出现这种情况的用例(即基于未定义行为的用法)。

我不确定我理解这个问题,但我认为你所问的需要sizeof()在相同类型上返回不同的值。例如,sizeof(foo.A)与sizeof(struct A)不同。因此,复制这些内容将非常困难。 - Charlie Burns
为什么它不重新排列成员?因为C编译器一开始就没有设计这样做。它按照你声明的顺序排列。C是一种相当低级的语言,可以更好地控制许多事情,如果编译器改变了东西,那将是不好的。如果我声明了成员A、B和C,我希望它们按照这个顺序在内存中排列。 - Jeff Mercado
@CharlieBurns,我的最后一段话是在试图解决这个问题。sizeof(foo.a)不需要与sizeof(struct A)不同。当然,如果sizeof(foo.a)返回8,则它包含其填充中的foo.c,但您无法访问该部分,因为它在结构体之外,并且访问它将是未定义的行为。那么,如果sizeof(foo.a)返回8会有什么问题呢? - Shahbaz
@JeffMercado,我并没有要求编译器重新排列成员。标准明确禁止这样做(C11,6.7.2.1.6)。 - Shahbaz
memcpy(&someC.a,&someA,sizeof(someC.a))不会覆盖someC.c吗?我想这就是答案,也是我在评论中提到的。 - Charlie Burns
@CharlieBurns,你说得对。这个反例比答案更加健壮!如果你写成一个答案,我会接受它。 - Shahbaz
2个回答

3
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)脚注如下:
因此,例如,结构分配不需要复制任何填充位。

sizeof(someC.a) == sizeof(someA) - Patrick B.
那是一个陷阱,没错。看起来标准可能已经排除了末尾的填充,并定义不应该复制该填充。但我想这可能会引入不必要的限制。 - Shahbaz
1
我接受其他答案,因为它提供了一个无条件中断的示例。 - Shahbaz

2
memcpy(&someC.a, &someA, sizeof(someC.a))会覆盖someC.c
我的评论意图是想表达sizeof()必须不同的问题。为了使memcpy()正常工作,sizeof(someC.a)必须与sizeof(someA)不同,这似乎会带来很多麻烦和难以发现的错误。请注意保留HTML标签。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接