为什么在C++中(C++)*(C++)未定义的行为?

问题描述:

我感到困惑这段代码的输出:为什么在C++中(C++)*(C++)未定义的行为?

int c=3; 
cout<<(c++)*(c++); 

我用gcc和输出为9,但有人说,这是不确定的行为,为什么呢?

+8

那么,这是c + +不是c – 2012-04-29 04:19:24

+0

相同的结果,相同的问题,无论是C或C++ – paulsm4 2012-04-29 04:29:29

+2

@ paulsm4不是。在C中,这是非常特定的行为:你会得到一个编译器错误。 – 2012-04-29 08:53:45

的问题是“序列点”:

http://en.wikipedia.org/wiki/Sequence_point

在命令式编程的序列点定义在它被保证在 计算机程序的执行的任何一点,所有的边以前 影响评估已经完成,并且没有任何方面的后续评估影响尚未执行。

当单个表达式中多次修改相同变量 时,序列点也会发挥作用。一个经常引用的例子是 C表达式i = i ++,它明显地指定了它以前的 值并且增量i。 i的最终值不明确,因为 取决于表达式评估的顺序,所以增量可能发生在赋值之前,之后或交错。特定语言的定义可能会指定其中一个可能的行为,或者简单地说行为是未定义的。在C和C++中, 评估这样的表达式会产生未定义的行为。[1]

碰巧,我在MSVC(Windows)和gcc(Linux)上得到了完全相同的答案 - “9”。我也收到了警告我是否与海湾合作委员会(“C”)或g ++(C++)编译:

$ g++ -o tmp -Wall -pedantic tmp.cpp 
tmp.cpp: In function "main(int, char**)": 
tmp.cpp:7: warning: operation on "c" may be undefined 
$ ./tmp 
c=9... 
+0

而在'tcc'和'clang'我得到'12'。 :) – Jack 2012-04-29 04:50:42

的C时的参数使许多事情没有意义,他们几乎任凭谁实现了*裁量权语言(即编写一个编译器)。在这些未定义的事情中,这些表达的各个部分的评估顺序是。

例如,是先乘后计算乘积++ es,还是先计算一个++再乘以,那么其他++

+0

评估顺序不是未定义的行为 - 这是未指定的行为。不同之处在于编译器无法做任何想要的事情 - 它仍然需要正确评估表达式。它不需要以任何特定顺序执行评估(除了某些操作会强制执行命令)。另一方面,表达式'i + = ++ i'是未定义的 - 这里*没有*正确的行为,编译器可以*地执行任何想要的操作。 – 2012-04-29 09:16:16

未定义的行为意味着任何东西都可能发生。

输出为9,但使用不同的编译器或不同的编译器开关,它也可能是1202147483647

+0

它可能会格式化您的硬盘驱动器! – orlp 2012-04-29 04:52:07

根据如何编写编译器,使用什么标志来编译,月相的相位等,答案可能是9,16,20,或者它可能产生nasal demons。总是尽量避免混淆代码和未定义的行为。查看如何避免这种情况的顺序点。想着序列点,为什么你的榜样的

+0

好吧,我会咬人的。我期望9,12或16,那么你如何得到20? – 2012-04-29 08:55:24

+0

@Mr。李斯特:这是未定义行为的本质。 – 2012-04-29 22:02:54

一种方式既有unspecified behaviorundefined behavior是通过考虑首先介绍了临时变量的实现:

这样的实现可能会处理后增量如下:

tmp_1=c;    // read 'c' 
tmp_2 = tmp_1 + 1; // calculate the incremented value 
c = tmp_2;   // write to 'c' 
tmp_1;    // the result of the expression 

原表达(c++)*(c++)有两个序列:

lhs_1=c;    // read 'c' 
lhs_2 = lhs_1 + 1; // calculate the incremented value 
c = lhs_2;   // write to 'c' 
lhs_1;    // the resulting value of the expression 

rhs_1=c;    // read 'c' 
rhs_2 = rhs_1 + 1; // calculate the incremented value 
c = rhs_2;   // write to 'c' 
rhs_1;    // the resulting value of the expression 

的顺序可以是:

lhs_1=c;    // read 'c' 
lhs_2 = lhs_1 + 1; // calculate the incremented value 
c = lhs_2;   // write to 'c' 

rhs_1=c;    // read 'c' 
rhs_2 = rhs_1 + 1; // calculate the incremented value 
c = rhs_2;   // write to 'c' 

lhs_1 * rhs_1   // (3 * 4) new value of 'c' is 5 

或者:

lhs_1=c;    // read 'c' 
rhs_1=c;    // read 'c' 

lhs_2 = lhs_1 + 1; // calculate the incremented value 
c = lhs_2;   // write to 'c' 

rhs_2 = rhs_1 + 1; // calculate the incremented value 
c = rhs_2;   // write to 'c' 

lhs_1 * rhs_1   // (3 * 3) new value of 'c' is 4 

或者:

rhs_1=c;    // read 'c' 
rhs_2 = rhs_1 + 1; // calculate the incremented value 
c = rhs_2;   // write to 'c' 

lhs_1=c;    // read 'c' 
lhs_2 = lhs_1 + 1; // calculate the incremented value 
c = lhs_2;   // write to 'c' 

lhs_1 * rhs_1   // (4 * 3) new value of 'c' is 5 

....等。

unspecified behavior是它可以先评估lhs或rhs。 undefined behavior是我们正在读写c,没有中间顺序点。