我对此代码的输出感到困惑:
int c=3;
cout<<(c++)*(c++);
我使用 gcc 并且输出是9
,但是有人说这是未定义的行为,为什么?
问题是“序列点”:
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...
未定义的行为意味着任何事情都可能发生。
输出是9
,但使用不同的编译器或不同的编译器开关,它也可能是12
,0
或2147483647
。
C规范留下了许多未定义的东西,它们几乎由实现该语言的人(即编写编译器)自行决定。在这些未定义的事物中,有表达式的各个部分的求值顺序。
例如,是先计算乘法,然后再计算两个++
es,还是++
先计算一个,然后计算乘法,然后再计算另一个++
?
根据编译器的编写方式、编译时使用的标志、月相等,答案可能是 9、16、20,也可能会产生鼻恶魔。始终尽量避免混淆代码和未定义的行为。查找有关如何避免这种情况的序列点。
考虑序列点以及为什么您的示例同时具有这两者的一种方法unspecified behavior
是undefined 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