5

我了解 C 使用序列点的概念来识别模棱两可的计算,并且该=运算符不是序列点。但是,我在执行该语句时看不到任何歧义

i = ++i

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

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

我是否遗漏了一些有关=功能的信息?

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

4

2 回答 2

7

你错过了一些关于未定义行为的东西。未定义的行为仅仅意味着编译器可以为所欲为。它可以抛出一个错误,它可以(就像 GCC 一样)显示一个警告,它可以让恶魔从你的鼻子里飞出来。首要的是,它的行为不会很好,并且在编译器之间的行为也不会一致,所以不要这样做!

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

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

该标准不保证不会发生这种情况,所以不要这样做!

于 2014-11-12T19:06:01.430 回答
3

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

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

那么,我们担心的副作用是什么?

  • ++i,它为 i 赋值i+1
  • i = ++i,它赋予 i 表达式的值++i,即i+1

因此,我们将获得两个(不可否认,等效的)副作用:分配i+1给变量i. 我们关心的是,这些副作用发生在哪两个序列点之间?

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

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

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


但是为什么在序列点之间多次修改一个变量是不好的呢?为了防止出现以下情况:

i = ++i + 1;

在这里,是递增的,但由于预增量的语义,i它也被分配了 value 。(i+1) + 1由于副作用的顺序不明确,因此行为未定义。

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

于 2014-11-12T19:56:14.227 回答