【问题标题】:Avoidable padding with "unfull" structs使用“不完整”结构的可避免填充
【发布时间】:2013-11-16 15:53:37
【问题描述】:

我试图给this answer 提供一个“合乎逻辑”的反例,表明当我遇到在我看来不合逻辑的情况时,根据它们的大小对结构的成员进行排序会最小化填充。

想象以下结构:

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 C 中,struct A 的填充是不必要的,c 可以放置在 a 的填充本来可以放置的位置,并且与 db 相同。

为什么编译器不通过将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) 不同。所以复制这些东西真的很困难。
  • 为什么不重新排列成员?因为 C 编译器最初并不是为此而设计的。它是按照您声明它们的顺序排列的。 C 是一种相当低级的语言,让我们可以更好地控制很多事情,如果编译器改变了事情会很糟糕。如果我声明成员 ABC,我希望它们按顺序排列在内存中。
  • @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 吗?我想这与答案相同,以及我的评论所要表达的意思。

标签: c


【解决方案1】:
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) 例如,结构赋值不需要复制任何填充位。

【讨论】:

  • sizeof(someC.a) == sizeof(someA)
  • 这是一个陷阱,对。似乎标准最终可能排除了填充,并定义不应复制该填充。但我想这可能会引入不必要的限制。
  • 我接受另一个答案,因为它提供了一个无条件中断的示例。
【解决方案2】:

memcpy(&someC.a, &someA, sizeof(someC.a)) 会覆盖someC.c

这就是我对sizeof() 的评论所要表达的意思 与众不同。要让memcpy() 工作,sizeof(someC.a) 必须 与sizeof(someA) 不同,后者似乎要求很多 麻烦且很难找到错误。

【讨论】:

    猜你喜欢
    • 2015-04-05
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 2010-10-26
    • 1970-01-01
    相关资源
    最近更新 更多