可能重复:
未定义的行为和序列点
我不确定这是否是一个 gcc bug,所以我会问:
unsigned int n = 0;
std::cout << n++ << n << ++n;
gcc 给出了极其奇怪的结果: AFAICT不可能的“122”。因为<< is left associative, it should be the same as:
operator<<(operator<<(operator<<(std::cout, n++), n), ++n)
并且由于在评估参数之前和之后都有一个序列点,因此两个序列点之间的 n 永远不会被修改两次(甚至不会被访问)——因此它不应该是未定义的行为,而只是未指定的评估顺序。
因此 AFAICT 有效结果将是: 111 012 002 101
没有别的
计算参数和调用函数之间有一个序列点。 评估不同参数之间没有顺序点。
我们看一下最外层的函数调用:
operator<<(operator<<(operator<<(std::cout, n++), n), ++n)
论据是
operator<<(operator<<(std::cout, n++), n)
和
++n
未指定首先评估其中哪一个。 还允许在计算第二个参数时部分计算第一个参数。
来自标准第
[intro.execution]
节(草案 3225 中的措辞):
如果 A 之前未排序 B和B在A之前未排序,那么A和B是未排序。 [ 注意: 未排序的执行 评估可能会重叠。 — 尾注 ]
除非另有说明,单个运算符的操作数和单个运算符的子表达式的求值 表达式是无序的。 [ 注意: 在执行过程中多次求值的表达式中 对于一个程序,其子表达式的无序和不确定顺序的求值不需要 在不同的评估中表现一致。 — 尾注 ] 操作数的值计算 运算符在运算符结果的值计算之前排序。如果对标量有副作用 相对于同一标量对象上的另一个副作用或值计算,对象是无序的 使用相同标量对象的值,行为是未定义的。
因为您对同一个标量对象有多个具有副作用的操作,并且这些操作彼此之间没有顺序,所以您处于未定义行为的领域,甚至
999
也是允许的输出。
编译器错误的第一条规则:这可能不是编译器错误,而是您的误解。在同一语句中使用后缀和前缀运算符会导致未定义的行为。尝试使用
-Wall
选项向您提供更多警告并向您展示代码中的潜在陷阱。
让我们看看当我们请求有关
test.cpp
的警告时,GCC 4.2.1 告诉我们什么:
#include <iostream>
int main() {
unsigned int n = 0;
std::cout << n++ << n << ++n << std::endl;
return 0;
}
当我们编译时:
$ g++ -Wall test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:5: warning: operation on ‘n’ may be undefined
你的代码是为什么在一些书中评论有经验的程序员不喜欢 that(++,--) 运算符重载的一个例子,甚至其他语言(ruby)还没有实现 ++ 或 --.