int a, b;
a = 1;
a = a + a++;
a = 1;
b = a + a++;
printf("%d %d, a, b);
输出:3,2
第3行和第5行有什么区别?
答案 0 :(得分:19)
你正在做的事情是未定义的。
您无法更改要分配给的变量的值。
你也不能改变带有副作用的变量的值,也尝试在同一个表达式的其他地方使用同一个变量(除非有sequence point,但在这种情况下没有)。 +的两个参数的评估顺序是未定义的。
因此,如果两行之间存在差异,那么第一行是未定义的,原因有两个,而第5行只是因为一个原因而未定义。但重点是第3行和第5行都是未定义的,并且要么做错了。
答案 1 :(得分:6)
你在第3行所做的事情是未定义的。 C ++具有“序列点”的概念(通常由分号分隔)。如果你在每个序列点修改一个对象不止一次,这是非法的,正如你在第3行所做的那样。正如section 6.5 of C99所说:
(2)在前一个和下一个序列点之间,一个对象应具有其存储的值 通过表达式的评估最多修改一次。此外,先前的价值 应该只读以确定要存储的值。
由于第二句,第5行也未定义。您阅读a
以获取其值,然后将其用于a++
中的其他作业。
答案 2 :(得分:4)
第3行未定义,第5行未定义。
正如Prasoon正确指出的那样,两者都是UB。
由于以下原因,未定义简单表达式a + a++
:
+
不是序列点,因此每个操作数的副作用可能以任何顺序发生。a
最初为1
。可能出现两种可能的[合理]情景之一:
首先评估第一个操作数,a
,
a)其值1
将存储在寄存器R
中。没有副作用。
b)评估第二个操作数a++
。它也计算为1,并被添加到同一寄存器R
。作为副作用,a
的存储值设置为2。
c)当前位于R
的添加结果将写回a
。 a
的最终值为2。
首先评估第二个操作数a++
。
a)它被评估为1
并存储在寄存器R
中。 a
的存储值增加到2
。
b)读取第一个操作数a
。它现在包含值2
,而不是1
!它已添加到R
。
c)R
包含3
,此结果将写回a
。添加的结果现在是3
,而不是2
,就像我们的第一种情况一样!
简而言之,您不能依赖此类代码。
答案 3 :(得分:4)
a++
是一个修复后的操作符,它获取的值然后递增它。
因此,对于2,3行:
a = 1
a = 1 + 1,a递增
a变为3 (注意,执行这些操作的顺序可能因编译器而异,并且a也很容易变为2)
:
a = 1
b = 1 + 1,a递增
b变为2,a变为2. (由于未定义的行为,b也可能变成a ++中的3个在a之前处理)
请注意,除了理解后缀运算符的工作方式之外,我真的不建议使用这个技巧。使用不同的编译器进行编译时,undefined behavior和 会得到不同的结果
因此,这不仅是一种不必要的混乱方式,而是一种不可靠,最糟糕的做法。
编辑:还有其他人指出,这实际上是未定义的行为。