为什么零长度VLA UB?

Jas*_*son 9 c arrays variable-length-array

2011年标准明确规定......

6.7.6.2数组声明符

  1. 如果size是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为替换为 *; 否则,每次评估它时,其值应大于零.可变长度数组类型的每个实例的大小在其生命周期中不会改变.如果size表达式是运算sizeof符操作数的一部分,并且更改size表达式的值不会影响运算符的结果,则无法指定是否计算size表达式.

这是设计,但以下代码似乎是合理的.

size_t vla(const size_t x) {

  size_t a[x];
  size_t y = 0;

  for (size_t i = 0; i < x; i++)
    a[x] = i;

  for (size_t i = 0; i < x; i++)
    y += a[i % 2];

  return y;
}
Run Code Online (Sandbox Code Playgroud)

Clang似乎为它生成合理的x64程序集(没有优化).显然索引零长度VLA没有意义,但访问超出边界会调用未定义的行为.

为什么零长度数组未定义?

use*_*ica 6

int i = 0;
int a[i], b[i];
Run Code Online (Sandbox Code Playgroud)

a == b吗?它不应该 - 它们是不同的对象 - 但避免它是有问题的.如果你在无条件ab无条件之间留下空隙,你就会浪费空间i > 0.如果你检查是否i == 0只留下间隙,那么你就是在浪费时间i > 0.

多维数组会变得更糟:

int i = 0;
int a[2][i];
Run Code Online (Sandbox Code Playgroud)

你可以在两个变量之间填充,但你可以在哪里填充?没有破坏不变量就没有办法做到这一点sizeof (int[2][i]) == 2 * i * sizeof (int).如果不这样做垫,然后a[0]a[1]具有相同的地址,你打破一个不同的重要不变.

这是一个不值得定义的头痛.


hac*_*cks 1

看看C标准:

C11- 6.7.6.2 数组声明符 (p1):

[...] 如果表达式是常量表达式,则其值大于零。[...]

(第5页):

如果大小是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为被*;替换。否则,每次评估时,其值都应大于零。[...]

4. 符合性:

如果违反了约束或运行时约束之外出现的“应”或“不应”要求,则行为是未定义的。未定义的行为在本国际标准中还用“未定义的行为”一词或省略任何明确的行为定义来表示。这三者的侧重点没有区别;它们都描述了“未定义的行为”

因此,声明零大小的数组会导致程序的未定义行为。

  • “因此,C 不允许数组长度为零。” - 为什么?那里的解释有很大的跳跃。 (3认同)