为什么 i = ++i + 2 是未定义行为?

3
我读到了有关“求值顺序”的内容,我理解了一些由于求值顺序引起的错误。
我的基本规则来自于一篇文本和示例:
- 操作数求值顺序与优先级和结合性无关。 - 在大多数情况下,顺序是未指定的。
因此,对于这样的表达式:int i = f1() * f2(); f1和f2必须在乘法运算之前被调用。毕竟,它们的结果是相乘的。然而,我们无法知道f1会在f2之前调用还是反过来。
未定义行为示例:
int i = 0;
cout << i << " " << ++i << endl; 

我理解的方式是:我把 i++i 当作一个函数。我不知道哪个先评估,所以第一个 i 可以是 01,这条规则很有意义。
while(beg != s.end())
    *beg = toupper(*beg++);  //Just another example.

我认为理解这个问题的关键是把每个操作数视为“评估单元”,并且不知道这些单元内部的评估顺序,但可以知道每个单元中的顺序。
但对于i = ++i + 2参考资料在这里, 为什么它是错误的? 我无法用自己的结论来解释。
左边的i被用作左值,而不是指针。 ++i只是重写原始值,并不会改变存储地址。如果它先评估还是后评估有什么错误呢?我的规则在这里失败了。
非常长,但尝试提供足够的背景信息,感谢您的耐心。

我不知道答案中经常提到的"sequence point"(顺序点)是什么,所以我认为我需要先了解一些关于它的内容。顺便说一下,这个争论对于一个新手来说并不是很有帮助,他只是想知道为什么在C++11之前被认为是错误的。


我发现这个答案未定义行为和序列点阐述了在C++11之前为什么i = ++i + 2是未定义行为。

可能是 C++11 中的新序列点 的重复问题。 - M.M
2个回答

4
C++11有新的排序规则。特别是对于前置增量(++i),副作用(写入新值)在使用新的增加值之前进行排序。由于赋值i=在其右侧的评估之后进行排序,这意味着写入++i在传递排序之前写入i=(++i + 2)
对于i=(i++ + 2)会有不同的情况。对于后置增量,副作用在排序之后进行,这意味着两个赋值不再相对排序。那是未定义的行为。

-3

i = ++i + 2 中,你的两个“子函数”是由 = 运算符进行的显式赋值和由 ++ 运算符进行的隐式赋值。

预增运算符定义为返回变量的递增值,这肯定应该用于加法(由 + 运算符执行)。但是,它并没有定义当递增值应何时存回到 i 中。

因此,未定义 i 的最终值是 old_i 递增加 2 还是仅递增了old_i


然而,在C++11之前是没有定义的,增加后的值应该何时存回i。 - Rakete1111
@CiaPan - 哎呀,我们两个都错了。我已经删除了我的回答。将+1转换为-1只是一个微不足道的编辑。 - Bathsheba
不存在“未定义值是X还是Y”的情况。如果值可以是X或Y,则称为未指定行为,而这不是。未定义行为意味着任何事情都可能发生。此外,您使用过去时态,但未指定行为从未定义到定义的时间。 - M.M
@M.M 至少有三个在我之前的回答和评论中描述了什么时候发生了变化,所以我不需要再次指明。我的目的是展示 'undefined' 状态来自哪里。 - CiaPan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接