2011年标准明确规定......
6.7.6.2数组声明符
- 如果size是一个不是整数常量表达式的表达式:如果它出现在a中 在函数原型范围内的声明,它被视为被替换为
醇>*
;除此以外, 每次评估时,它的值应大于零。每个实例的大小 可变长度数组类型在其生命周期内不会改变。如果size表达式是sizeof
运算符的操作数的一部分,并且更改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;
}
Clang似乎为它生成合理的x64程序集(没有优化)。显然索引零长度VLA并没有意义,但访问超出边界会调用未定义的行为。
为什么零长度数组未定义?
答案 0 :(得分:6)
int i = 0;
int a[i], b[i];
a == b
?它不应该是 - 它们是不同的对象 - 但避免它是有问题的。如果您无条件地在a
和b
之间留出空隙,则会在i > 0
案例中浪费空间。如果您检查i == 0
是否只留下差距,那么您就会在i > 0
案例中浪费时间。
多维数组会变得更糟:
int i = 0;
int a[2][i];
你可以在两个变量之间填充,但你可以在哪里填充?如果不打破sizeof (int[2][i]) == 2 * i * sizeof (int)
的不变量,就无法做到这一点。如果您没有填充,则a[0]
和a[1]
具有相同的地址,并且您正在打破其他重要的不变量。
这是一个不值得定义的头痛。
答案 1 :(得分:4)
虽然我们可以看到gcc supports zero length arrays an extension,但显然它们很有用。从标准的角度来看,它似乎会产生一些问题,因为现在每个对象都应该有一个唯一的地址。我们可以从C99和C11标准草案6.5.9中的平等运算符中看出这一点:
两个指针比较相等,当且仅当两个都是空指针时,两个指针都指向 相同的对象(包括指向对象和开头的子对象的指针)或函数, 两者都是指向同一个数组对象的最后一个元素之后的指针,或者一个是指针 到一个数组对象的末尾,另一个是指向另一个数组对象的开头的指针 紧接着地址中第一个数组对象的数组对象 space.94)
所以这需要一些特殊的外壳,并且可以使用替代方法提供大多数有用的功能,例如flexibile数组。
它也可能需要在其他地方进行更改,如M.M.在6.3.2.1
Lvalues,数组和函数指示符中指出数组到指针衰减:
[...]表达有 类型''数组''转换为类型为''指向类型'指针的表达式 到数组对象的初始元素,而不是左值[...]
这似乎需要进行一些非平凡的改变才能获得最小的额外收益。
答案 2 :(得分:1)
看C标准:
[...]如果表达式是常量表达式,它 的值应大于零。 [...]
如果size是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为由
*
替换;否则,每次评估时, 的值应大于零。 [...]
如果&#34;应该&#34;或&#34;不得&#34;违反约束或运行时约束之外的要求,行为未定义。未定义的行为在本国际标准中以“未定义的行为”字样表示。或遗漏任何明确的行为定义。 这三者的重点没有区别;他们都描述了未定义的行为&#34; 。
因此,声明零大小的数组会导致程序的未定义行为。