【问题标题】:Can C arrays contain padding in between elements?C 数组可以在元素之间包含填充吗?
【发布时间】:2010-11-07 04:34:41
【问题描述】:

我听说在 C 语言中,包含在结构中的数组可能在数组元素之间添加了填充。现在很明显,任何一对元素之间的填充量不能变化,或者用简单的指针算法计算数组中的下一个元素是不可能的。

这个谣言还说不包含在结构中的数组保证不包含填充。我至少知道那部分是真的。

所以,在代码中,谣言是:

{
    // Given this:
    struct { int values[20]; } foo;
    int values[20];

    // This may be true:
    sizeof(values) != sizeof(foo.values);
}

我很确定sizeof(values) 将始终等于sizeof(foo.values)。但是,我无法在 C 标准(特别是 C99)中找到任何明确确认或否认这一点的内容。

有谁知道这个谣言是否在任何 C 标准中得到解决?

edit:我知道在数组foo.values 的末尾和结构foo 的末尾之间可能有填充,并且标准规定之间不会有填充foo 的开头和 foo.values 的开头。但是,有没有人引用引用标准,它说foo.values的元素之间没有填充?

【问题讨论】:

  • 我没有任何参考资料,但我很确定我已经读过编译器可以随意对齐结构。一般来说,这可能会以 cletus 描述的方式完成。
  • 似乎很多人都错过了比较是在两个数组之间而不是在数组/结构之间。 sizeof(值) != sizeof(foo);这不是他所做的,而是人们似乎在某些答案中的想法

标签: c arrays padding


【解决方案1】:

以下是关于为什么结构可能需要在其成员之间或什至在其最后一个成员之后进行填充的解释,以及为什么数组不需要:

不同的类型可能有不同的对齐要求。一些类型需要在字边界上对齐,其他类型需要在双字甚至四字边界上对齐。为了实现这一点,一个结构可能在其成员之间包含填充字节。可能需要尾随填充字节,因为直接位于结构中的内存位置也必须符合结构的对齐要求,即如果bar 的类型为struct foo *,则

(struct foo *)((char *)bar + sizeof(struct foo))

产生一个指向struct foo 的有效指针(即不会因为未对齐而失败)。

由于数组的每个“成员”都具有相同的对齐要求,因此没有理由引入填充。这也适用于结构中包含的数组:如果数组的第一个元素正确对齐,那么所有后续元素也是如此。

【讨论】:

    【解决方案2】:

    不,数组元素之间永远不会有填充。这是明确不允许的。 C99 标准调用数组类型“数组类型描述了一组连续分配的非空对象......”。相比之下,结构是“顺序”分配的,而不是“连续”分配的。

    结构中的数组之前或之后可能有填充;那完全是另一种动物。编译器可能会这样做以帮助对齐结构,但 C 标准没有说明这一点。

    【讨论】:

    • 取决于你的意思。编译器可以在 struct 的末尾添加填充(参见我的示例)。如果您采用这样的结构,并为其创建一个数组,则在使用的字节之间会有未使用的字节,但数组的每个元素始终是前一个元素之后的 sizeof(T) 个字节。但是 sizeof(T.a) + sizeof(T.b) + ... 可能不等于 sizeof(T)。
    • "你进行的 sizeof 比较可以得到这个结果" 不是吗? compair 在两个数组之间,而不是在数组和结构之间
    • @Thanatos - 我基本上同意,但如果编译器在结构的末尾添加字节,则包含在 sizeof 中。 Sizeof 在一个类型上,而不是该类型的实例,并且该类型不知道它是否在数组中使用。
    • 感谢您的报价。它来自第 6.2.5 节第 20 段,供有兴趣查找的人参考。
    【解决方案3】:

    是的,有点。变量通常与某个边界对齐,具体取决于变量。以以下为例:

    typedef struct
    {
        double d;
        char c;
    } a_type_t;
    

    double 和 char 在我的系统上分别是 8 个字节和 1 个字节。总共 9 个。然而,该结构将是 16 个字节,因此双精度数始终是 8 字节对齐的。如果我刚刚使用整数、字符等,那么对齐可能是 1、2、4 或 8。

    对于某些类型 T,sizeof(T) 可能等于也可能不等于 sizeof(T.a) + sizeof(T.b) + sizeof(T.c) ... 等等。

    通常,这完全取决于编译器和架构。在实践中,这并不重要。

    【讨论】:

    • @Rom 为什么没关系?我认为这个问题与虚假分享的问题直接相关。例如 - 当有一个结构数组甚至是基本类型,并且每个线程都有一个在这个数组中分配给该线程经常写入的条目时。然后将数组元素保持在不同的缓存行会很有用,否则每次写入都可能意味着当前写入线程中的缓存未命中。
    【解决方案4】:

    考虑:

    struct {
      short s;
      int i;
    } s;
    

    假设 short 是 16 位,而您使用的是 32 位,则大小可能为 8 个字节,因为每个结构成员倾向于对齐一个字(在本例中为 32 位)边界。我说“可能”是因为它是特定于实现的行为,可以通过编译器标志等来改变。

    值得强调的是,这是 C 标准不一定定义的实现行为。很像 shorts、ints 和 longs 的大小(C 标准简单地说 shorts 不会大于 int,longs 不会小于 int,最终可能为 16/32/32、16/32/64 , 32/32/64 或许多其他配置)。

    【讨论】:

    • 在基于 ARM 的机器上可以,但也很危险,因为 ARM 不执行对齐检查
    • 实际上,我相信在 x86 和 amd64 机器上,对于 gcc 编译器来说,大小为 6 —— char 对齐 8 字节没有任何好处。但正如你提到的,这取决于编译器。
    • 在 x86 和 amd64 机器上使用 gcc 的大小为 8。我同意这取决于编译器;在此答案的示例中,问题是 int 的自然对齐。
    【解决方案5】:

    这里要小心。可以在结构的末尾添加填充,但不会像您在问题中所说的那样在数组的元素之间添加填充。数组将始终引用连续的内存,尽管结构数组可能会将填充添加到每个元素作为结构本身的一部分。

    在您的示例中,valuesfoo.values 数组将具有相同的大小。任何填充都将成为结构 foo 的一部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 2014-07-02
      • 1970-01-01
      相关资源
      最近更新 更多