在C++(或C)中对变量进行多次前缀递增操作

15

为什么以下代码在C++中可以编译通过?

int phew = 53;
++++++++++phew ;

同样的代码在C语言中失败了,为什么?


只是为了好玩加上了C++0x标签。 :) - Prasoon Saurav
1
必须有一个标准问题可以参考这种类型的问题。因此,所有这类问题(我们从新大学生那里得到的)都可以快速关闭并标记为“阅读此内容”。 - Martin York
2个回答

28
注意:了解下面的行为基础理由非常重要,这需要理解两个缺陷报告DR#637DR#222
为了解释,在C++0x中有“值计算”和“副作用”。例如,副作用是赋值,而值计算是确定lvalue引用的内容或从lvalue读取值。请注意,C++0x不再具有序列点,并且这些内容以“序列化之前”/“序列化之后”的术语表述。并且声明如下:
如果标量对象上的副作用与同一标量对象上的另一个副作用或使用同一标量对象的值计算不序列化,则行为未定义。
++v等同于v += 1,等同于v = v + 1(除了v仅被评估一次)。这导致++(v = v + 1),我将其写为inc = inc + 1,其中inc指的是v = v + 1的lvalue结果。
在C++0x中,++ ++v不是未定义行为,因为对于a = b,分配在b和a的值计算之后,但在分配表达式的值计算之前。由此可知,v = v + 1中的分配在inc的值计算之前被序列化。而inc = inc + 1中的赋值在inc的值计算之后被序列化。最终,两个分配都将被序列化,因此没有未定义的行为。

3
好的回答。同样地,int a=4; ++a=5; 在 C++0x 中也不会导致未定义行为,对吗? - Prasoon Saurav
3
@Johannes: 我不明白为什么 a = ++a 不会引起未定义行为,而 a = a++ 会。根据 "除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值顺序以及副作用发生的顺序是未指定的",发生了什么? - Daniel Trebbien
5
@Daniel,第二个例子会引发未定义行为,因为在“a++”中对a的修改不是在值计算之前被排序的,而是在之后(很自然,因为您想要它产生旧值)。因此,“a ++”中的赋值和修改彼此之间没有排序。您引用的文本现在的措辞是:“除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的评估未排序。” - Johannes Schaub - litb
3
@Prasoon,“a++”中的修改在“a++”的值计算之后进行排序。在“a + a++”中,“a”的两个部分没有以任何方式排序,无论它们的值计算还是副作用都没有排序。因此,你有一个副作用(对“a”的修改),相对于该“a”的值计算(第一个操作数),这个副作用是没有顺序的,导致了未定义行为。 - Johannes Schaub - litb
1
a = a++; 所涉及的步骤包括:a)计算“a”的左值(lhs)的值;b)计算“a”的右值(rhs)的值;c)++的副作用;d)赋值的副作用。您的意思是在C++0x中,步骤(d)和(c)是无序的吗? - Chubsdad
显示剩余16条评论

15

这是因为在C++中,前置自增运算符返回一个lvalue,它要求它的操作数是一个lvalue

++++++++++phew ;被解释为++(++(++(++(++phew))))

然而,你的代码调用了未定义行为,因为你试图在两个序列点之间多次修改phew的值。

C中,前置自增运算符返回一个rvalue,并要求它的操作数是一个lvalue。因此,在C模式下,你的代码无法编译。


2
@Prasoon:不是想质疑你,只是好奇想了解一下你所说的:“你试图在两个序列点之间多次修改phew的值”。你能提供一个标注让我可以阅读更多关于这方面的内容吗? - Merlyn Morgan-Graham
3
@Merlyn Morgan-Graham: 阅读 Steve Summit 的这篇文章:http://c-faq.com/expr/seqpoints.html 。 - Prasoon Saurav
2
只是为了明确起见,对于用户定义的类型,++++i 是有定义的,对吗? - fredoverflow
2
@FredOverflow:是的,因为函数调用引入了一个序列点。 :) - Prasoon Saurav
9
这只是在C++03中不被允许的。但在C++0x中是合法的。 - Johannes Schaub - litb
显示剩余6条评论

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