4

我对此代码的输出感到困惑:

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

我使用 gcc 并且输出是9,但是有人说这是未定义的行为,为什么?

4

5 回答 5

4

问题是“序列点”:

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

命令式编程中的顺序点定义了计算机程序执行中的任何点,在该点处,可以保证先前评估的所有副作用都已执行,并且尚未执行后续评估的副作用。

当同一个变量在一个表达式中被多次修改时,序列点也会起作用。一个经常被引用的例子是 C 表达式 i=i++,它显然既赋予 i 其先前的值并增加 i。i 的最终值是不明确的,因为根据表达式求值的顺序,增量可能发生在赋值之前、之后或与赋值交错。特定语言的定义可能会指定一种可能的行为,或者简单地说该行为是未定义的。在 C 和 C++ 中,评估这样的表达式会产生未定义的行为。 [1]

碰巧的是,我在 MSVC(Windows)和 gcc(Linux)上都得到了完全相同的答案——“9”。无论我使用 gcc ("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...
于 2012-04-29T04:20:37.550 回答
3

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

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

于 2012-04-29T04:20:46.297 回答
1

C规范留下了许多未定义的东西,它们几乎由实现该语言的人(即编写编译器)自行决定。在这些未定义的事物中,有表达式的各个部分的求值顺序。

例如,是先计算乘法,然后再计算两个++es,还是++先计算一个,然后计算乘法,然后再计算另一个++

于 2012-04-29T04:20:42.207 回答
0

根据编译器的编写方式、编译时使用的标志、月相等,答案可能是 9、16、20,也可能会产生鼻恶魔。始终尽量避免混淆代码和未定义的行为。查找有关如何避免这种情况的序列点。

于 2012-04-29T04:22:05.420 回答
0

考虑序列点以及为什么您的示例同时具有这两者的一种方法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

....ETC。

unspecified behavior就是它可以先评估 lhs 或 rhs 。就是我们在没有中间序列点的情况下undefined behavior进行读写。c

于 2012-12-18T10:01:51.347 回答