a [a [0]] = 1是否会产生未定义的行为?

这个C99代码是否会产生未定义的行为?

#include <stdio.h>

int main() {
  int a[3] = {0, 0, 0};
  a[a[0]] = 1;
  printf("a[0] = %dn", a[0]);
  return 0;
}

在声明中a[a[0]] = 1;a[0]都被读取和修改。

我看了ISO / IEC 9899的n1124草案。它说(在6.5表达式中):

在前一个和下一个序列点之间,一个对象应该通过评估一个表达式最多修改其存储值一次。 此外,先验值只能读取以确定要存储的值。

它没有提到读取对象来确定要修改的对象本身。 因此这种说法可能会产生未定义的行为。

但是,我感到很奇怪。 这实际上是否会产生未定义的行为?

(我也想在其他ISO C版本中了解这个问题。)


先前值应该只读取以确定要存储的值。

这有点模糊,并造成混乱,这就是C11为什么抛出它并引入新的测序模型的部分原因。

它试图说的是:如果读取旧值保证发生的时间早于写入新值,那就没有问题了。 否则它是UB。 当然,这是要求在写入之前计算新值。

(当然,我刚刚写的描述会被一些人发现比标准文本更模糊!)

例如x = x + 5是正确的,因为在不知道x情况下无法计算出x + 5 。 然而a[i] = i++是错误的,因为i不需要读取左边的i来计算出存储在i的新值。 (这两个读取的i分别考虑)。


现在回到您的代码。 我认为这是明确定义的行为,因为读取a[0]以确定数组索引是保证在写入之前发生的。

在我们确定要写什么之前,我们不能写。 直到读完a[0]之后,我们才知道该写什么。 因此读取必须在写入之前进行,所以没有UB。

有人评论序列点。 在C99中,这个表达式中没有序列点,所以序列点不在这个讨论中。


这个C99代码是否会产生未定义的行为?

不会。它不会产生不确定的行为。 a[0]在两个序列点之间只修改一次(第一个序列点在初始化器的结尾int a[3] = {0, 0, 0};第二个序列在完整表达式a[a[0]] = 1 )。

它没有提到读取对象来确定要修改的对象本身。 因此这种说法可能会产生未定义的行为。

一个对象可以被读取多次以修改它自己,并且它是一个完美定义的行为。 看看这个例子

int x = 10;
x = x*x + 2*x + x%5;   

报价的第二个陈述说:

此外, 先验值只能读取以确定要存储的值。

读取上述表达式中的所有x来确定对象x本身的值。


注:请注意,问题中提到的引号有两部分。 第一部分说:在前一个和下一个序列点之间,一个对象应该通过评估一个表达式来最多修改其存储值一次,
因此表达就像

i = i++;

属于UB(在前一个和下一个序列点之间的两个修改)。

第二部分说:此外,先验值应该只读取以确定要存储的值,因此表达式等

a[i++] = i;
j = (i = 2) + i;  

调用UB。 在这两种表情i被修改,只有一次一个和下一个顺序点之间,而最右边的阅读i并不确定的值存储在i


在C11标准中,这已被更改为

6.5表达式:

如果对标量对象的副作用与相同标量对象的不同副作用或使用相同标量对象的值进行值计算相反,则行为未定义。 [...]

在表达a[a[0]] = 1 ,仅存在一个副作用a[0]的索引和值计算a[0]是值计算之前测序a[a[0]]


C99列举了附录C中所有序列点的列举。在附录C中有一个

a[a[0]] = 1;

因为它是一个完整的表达式语句,但里面没有序列点。 尽管逻辑规定必须首先评估子表达式a[0] ,并且用于确定赋值给哪个数组元素的结果,但排序规则不能确保它。 当a[0]的初始值为0a[0]在两个序列点之间读取和写入,读取不用于确定要写入的值。 根据C99 6.5 / 2,评估表达式的行为因此是不确定的,但实际上我认为您不需要担心。

C11在这方面更好。 第6.5节第(1)段说

表达式是一系列运算符和操作数,用于指定值的计算,指定对象或函数,或者生成副作用或执行其组合。 运算符操作数的值计算在运算符结果的值计算之前排序。

特别要注意第二句,它在C99中没有类似之处。 你可能认为这样就足够了,但事实并非如此。 它适用于值计算,但它没有提到与计算值有关的副作用的排序。 更新左操作数的值是一个副作用,所以额外的句子不会直接应用。

尽管如此,C11对于我们来说也是如此,因为赋值运算符的规范提供了所需的排序(C11 6.5.16(3)):

[...]更新左操作数的存储值的副作用在左和右操作数的值计算之后被排序。 操作数的评估是不确定的。

(相反,C99只是说更新左操作数的存储值发生在前一个和下一个序列点之间)。将6.5节和6.5.16节放在一起,则C11给出了一个定义明确的序列:inner []是在存储值更新之前评估的外部[]之前进行评估。 这符合C11 6.5(2)的版本,因此在C11中,定义了评估表达式的行为。

链接地址: http://www.djcxy.com/p/73197.html

上一篇: Does a[a[0]] = 1 produce undefined behavior?

下一篇: In C++11, does `i += ++i + 1` exhibit undefined behavior?