2011年标准明确规定……
6.7.6.2数组声明符
这是设计的,但以下代码似乎是合理的。
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没有意义,但访问超出边界会调用未定义的行为。
为什么零长度数组未定义?
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]
具有相同的地址,并且你打破了一个不同的重要不变量。
这是一个不值得定义的头痛。
虽然我们可以看到gcc支持零长度数组扩展 ,但显然它们很有用。 从标准的角度来看,它似乎会产生一些问题,因为现在每个对象都应该有一个唯一的地址。 我们可以从C99和C11标准草案6.5.9中看到这一点。平等运营商说:
两个指针比较相等,当且仅当两个都是空指针时,两者都是指向同一对象的指针(包括指向对象的指针和在其开头的子对象)或函数,两者都是指向同一数组的最后一个元素之后的指针object,或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象的开头的指针,该数组对象恰好跟随地址空间中的第一个数组对象.94)
因此,这需要一些特殊的shell,并且可以使用替代方法提供大多数有用性,例如flexibile数组。
它也可能需要在其他地方进行更改,因为MM在6.3.2.1
Lvalues,数组和函数指示符中指出数组指针衰减:
[…]具有类型”数组类型”的表达式将转换为类型为”指向类型’的指针的表达式,指向数组对象的初始元素,而不是左值[… ]
这似乎需要几个非平凡的变化才能获得最小的额外收益。
看C标准:
C11- 6.7.6.2数组声明符(p1):
[…]如果表达式是常量表达式, 则其值应大于零 。 […]
(P5):
如果size是一个不是整数常量表达式的表达式:如果它出现在函数原型范围的声明中,则将其视为
*
被替换为*
; 否则, 每次评估它时,其值应大于零 。 […]
4.符合性:
如果违反了约束或运行时约束之外出现的“应该”或“不应该”的要求,则行为是不确定的 。 未定义的行为在本国际标准中以“未定义的行为”或省略任何明确的行为定义的方式另有说明。 这三者之间的重点没有区别; 他们都描述了“未定义的行为” 。
因此,声明零大小的数组会导致程序的未定义行为。
以上就是c/c++开发分享为什么零长度VLA UB?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/546017.html