c - 为什么我= ++我调用未定义的行为?

时间:2014-11-12 19:00:59

标签: c increment undefined-behavior operator-precedence sequence-points

我理解C使用序列点的概念来识别模糊计算,而=运算符不是序列点。但是,我无法看到执行语句

时有任何歧义

i = ++i

根据我的理解,这仅仅是评估&i处的任何内容,递增它并将其存储在同一位置。然而,GCC将其标记为:

[警告]'i'上的操作可能未定义[-Wsequence-point]

我是否遗漏了=如何运作?

编辑:在标记为重复之前,请注意我浏览了有关序列点和未定义行为的其他帖子。它们都没有专门针对表达式i=++i(请注意 pre -increment)。提到的表达通常是i=i++a=b++ + ++b等。我对其中任何一个都毫无疑问。

2 个答案:

答案 0 :(得分:5)

您遗漏了一些未定义的行为。未定义的行为只是意味着编译器可以做任何想做的事情。它可以抛出错误,它可以(如GCC所示)显示警告,它可以导致恶魔飞出你的鼻子。主要的是,它不会表现为并且它在编译器之间不会表现一致,所以不要这样做!

在这种情况下,编译器不必使得在返回语句的rhs之前必须完成运算符的lhs的副作用。这对你来说很有趣,但你不像计算机那样思考。如果需要,它可以计算返回值并将其返回到寄存器中,将其分配给i,然后对实际值执行增量。所以它看起来更像是

register=i+1;
i=register;
i=i+1;

标准不保证不发生这种情况,所以就这样做吧!

答案 1 :(得分:2)

出现未定义的行为是因为变量i在两个sequence points之间被多次修改。序列点是先前评估的所有副作用可见的点,但 no 未来的副作用是可见的。标准规定:

  

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

那么,我们关注的副作用是什么?

  • ++i为我分配值i+1
  • i = ++i,其中为我分配了表达式++i的值,即i+1

因此,我们将获得两个(诚然,等效)副作用:将i+1分配给变量i。我们关注的是,在哪两个序列点之间会发生这些副作用?

哪些操作构成序列点?有多个,但这里只有一个实际相关:

  • 完整表达式的末尾(在这种情况下,i = ++i是一个完整的表达式)

即,预增量++i 不是序列点。这意味着两个副作用(增量和赋值)将在相同的两个序列点之间发生,修改相同的变量i。因此,它是未定义的行为;两个修改碰巧具有相同价值的事实是无关紧要的。


但为什么在序列点之间多次修改变量是不好的?为了防止这样的事情:

i = ++i + 1;

这里,i递增,但由于预增量的语义,它也被赋值(i+1) + 1。由于副作用具有模糊的排序,因此行为未定义。

现在,假设标准中有一个特殊情况,即只要值相同,两个序列点之间的多次修改就可以了,但这可能会使编译器实现不必要地复杂化,而没有太大的好处。