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

Why is (c++)*(c++) undefined behavior in C++?

本文关键字:c++ 未定义 为什么 C++      更新时间:2023-10-16

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

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的,但使用不同的编译器或不同的编译器开关,它也可能是1202147483647

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

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

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

思考序列点以及为什么您的示例同时具有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