11

在偶然发现“为什么这些构造使用前增量和后增量未定义的行为? ”这个问题后,今天我决定获取下一个 C 标准的最新草案,并阅读更多关于它的信息。
在我在 C17 草案中发现以下段落后不久:

表达式是一系列运算符和操作数,它们指定值的计算,或指定对象或函数,或产生副作用,或执行它们的组合。运算符的操作数的值计算在运算符结果的值计算之前排序
来源:ISO/IEC 9899:2017,第 6.5 节第 1 节“表达式”(链接断开使用web.archive.org

现在我有点困惑。这不意味着这i = i++是定义的行为吗?我看了另一个草稿,这次是 C99:

表达式是一系列运算符和操作数,它们指定值的计算,或指定对象或函数,或产生副作用,或执行它们的组合。
来源:ISO/IEC 9899:1999,第 6.5 §1 节“表达式”

就是少了那句话!

问题

  1. 我是不是误会了什么?
  2. 答案是否已过时?
  3. 我看错草稿了吗?

注意:这个问题是相关的,但它是关于 C++ 的。

4

2 回答 2

7

您突出显示的段落仅表示表达式i++i在评估完整表达式之前进行评估i = i++。它仍然是未定义的行为,因为i在没有序列点的表达式中被多次修改。

该段落首次出现在C11中,因此与该版本 C17 没有任何变化。

于 2018-11-25T14:27:01.230 回答
2

完整的故事。在 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 中的“未排序”相反。

于 2018-11-26T08:03:29.610 回答