在今天遇到问题“Why are these constructs using pre and post-increment undefined behavior?”后,我决定抓住最新的草案,为我能找到的下一个C标准,并阅读更多关于它的内容。 在我在C17草案中发现以下段落后不久:
表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合。在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序 来源:ISO/IEC 9899:2017, Section 6.5 §1 "Expressions"(链接破坏使用web.archive.org)
现在我有点困惑。这是不是意味着i = i++
被定义为行为?我这次看了另一个草案,C99:
表达式是操作符和操作数的序列,其指定值的计算,或指定对象或函数,或者生成副作用,或执行其组合。 资料来源:ISO/IEC 9899:1999, Section 6.5 §1 "Expressions"
它错过了那句话!
您突出显示的段落仅表示在评估完整表达式i++
之前评估表达式i
和i = i++
。它仍然是未定义的行为,因为i
在没有序列点的表达式中被多次修改。
那篇文章首次出现在C11,所以C17版本没有变化。
全文。在C99中,我们将这个文本用于6.5.16赋值运算符:
更新左操作数的存储值的副作用应发生在前一个和下一个序列点之间。
操作数的评估顺序未指定。如果尝试修改赋值运算符的结果或在下一个序列点之后访问它,则行为未定义。
这在C11中改为:
在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。对操作数的评估是不确定的。
这只是不同(和更糟)的措辞,两个版本的行为相同 - 关键是C11部分中的最后一个句子仍然会产生这种未定义的行为,因为左操作数的评估仍然与右操作数相关。值计算仅指各个操作数。
C17与C11具有相同的文字。所以答案是:不,i = i++;
在C17中仍然是未定义的行为。
仅供参考,将其与C ++ 11(5.17)进行比较:
在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序。
这与C11大致相同,没有明确的“操作数的评估未被排序”。这是C ++ 11中的一个缺陷,目前尚不清楚这是否会使某些表达式定义明确。
C ++ 17提供了一个澄清(8.5.18):
在所有情况下,在右和左操作数的值计算之后,以及在赋值表达式的值计算之前,对赋值进行排序。右操作数在左操作数之前排序。
所以在C ++ 17中,i=i++;
肯定是定义明确的。正如我们所看到的,措辞是明确的,而不是C11 / C17中的“无序”。