【问题标题】:Can an array have trailing padding?数组可以有尾随填充吗?
【发布时间】:2014-11-23 19:28:20
【问题描述】:

很明显,C 中的数组cannot insert padding between their elements。但是,是否有任何规则说他们不能在整个数组的 end 处添加尾随填充?

即这个程序是否保证在任何地方都能给出相同的结果?

#include <stdio.h>
int main(void) {
    typedef char a[3];
    typedef a b[3];
    printf("%zu %zu\n", sizeof(a), sizeof(b)); // -> 3 9
}

据我所知,在a 的大小上添加一个或五个尾随字节,也许是在错误的优化尝试中,不会破坏数组访问规则(b[1][1] 仍然精确映射到 @987654325 @ 不管其包含的a 对象的大小如何,并且访问超出包含的a 的长度无论如何都是UB)。

我在 C 标准中找不到任何地方 数组的大小是元素类型的大小乘以元素的数量。 6.5.3.4 只说 sizeof 返回数组中的“字节数”(它确实给出了 sizeof array / sizeof array[0] 作为代码示例,但这只是一个示例 - 它并没有说它必须工作,它没有提供任何细节)。

隐式保证对于编写依赖于精确数据布局的可移植代码很有用,例如传递打包的 RGB 值:

typedef uint8_t RGB[3];
RGB * data = ...;
glColorPointer(3, GL_UNSIGNED_BYTE, 0, data);

(好吧,所以 OpenGL 可以接受步幅值,所以这是一个不好的例子,但你明白了)

就此而言,我假设从广泛的概念(甚至是标准中的示例)您可以使用sizeof 获得数组的元素数量,无论如何这很可能在任何地方都适用 - 是否存在任何已知的情况都不是吗?

【问题讨论】:

  • padding的意思是让数据对齐。在你的情况下,我确信所有 3 的 char 数组的大小都是 3。但是如果你声明一个 int 之后,可能会有 1 或 5 个填充。一个好的 c 编译器不会做额外的工作,使其难以预测。但是如果你在谈论可移植性,我认为最好在目标平台上再次编译代码,或者如果平台有很大差异,则交叉编译。
  • “它没有提供任何细节”——它确实说计算数组中元素的数量。没错,这只是一个例子,而且这些例子并不规范。
  • 通过查看后续子句可以间接推断出数组没有这样的填充:“当应用于具有结构或联合类型的操作数时,结果是这样的字节总数一个对象,包括内部和尾随填充。” – 这种对结构和联合中的填充的明确引用,以及在数组的情况下缺乏填充让我相信数组中不能有这样的填充。
  • 我无法想象填充数组的意义何在。元素已经正确对齐。
  • @ooga:在一种不允许像 C 那样进行指针操作的语言中,数组填充可能对“金丝雀”有用:如果编译器预先加载了一个数组的空间如果标记数据的已知模式,则可以定期检查此类数据以确保没有发生缓冲区溢出。或者,在某些系统上,可以在任何尝试覆盖进位地址时配置硬件陷阱,尽管此类功能通常仅限于故障排除目的(因为许多平台可用的陷阱数量非常有限)。

标签: c language-lawyer


【解决方案1】:

我认为,该标准从未认为有必要实际说明数组没有填充,原因很简单,绝对没有理由说明这种填充可能对任何实现都有用。

也就是说,我相信标准通过== 运算符的描述禁止这种填充。

6.5.9 等式运算符

语义

6 两个指针比较相等当且仅当 [...] 或一个是指针 一个指向一个数组对象的末尾,另一个是指向另一个数组对象的开头的指针,该数组对象恰好紧跟在地址空间中的第一个数组对象之后。

给定

int array[2][2];

表达式&amp;array[0][2]points 是一个超过第一个数组子对象末尾的指针。 &amp;array[1][0] 是指向第二个数组子对象的指针,它紧跟在内存中的第一个数组之后。这些指针需要比较相等。如果int[2] 有尾随填充,如果sizeof(int[2]) &gt; 2 * sizeof(int),我无法想象任何实现如何使两个指针比较相等。

【讨论】:

    猜你喜欢
    • 2017-03-06
    • 1970-01-01
    • 2017-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多