意外的求值顺序(编译器错误?)[重复]

问题描述 投票:0回答:3

可能重复:
未定义的行为和序列点

我不确定这是否是一个 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

没有别的

c++ gcc operator-precedence
3个回答
9
投票

计算参数和调用函数之间有一个序列点。 评估不同参数之间没有顺序点。

我们看一下最外层的函数调用:

operator<<(operator<<(operator<<(std::cout, n++), n), ++n)

论据是

  • operator<<(operator<<(std::cout, n++), n)

  • ++n

未指定首先评估其中哪一个。 还允许在计算第二个参数时部分计算第一个参数。

来自标准第

[intro.execution]
节(草案 3225 中的措辞):

  • 如果 A 之前未排序 BBA之前未排序,那么AB未排序。 [ 注意: 未排序的执行 评估可能会重叠。 — 尾注 ]

  • 除非另有说明,单个运算符的操作数和单个运算符的子表达式的求值 表达式是无序的。 [ 注意: 在执行过程中多次求值的表达式中 对于一个程序,其子表达式的无序和不确定顺序的求值不需要 在不同的评估中表现一致。 — 尾注 ] 操作数的值计算 运算符在运算符结果的值计算之前排序。如果对标量有副作用 相对于同一标量对象上的另一个副作用或值计算,对象是无序的 使用相同标量对象的值,行为是未定义的。

因为您对同一个标量对象有多个具有副作用的操作,并且这些操作彼此之间没有顺序,所以您处于未定义行为的领域,甚至

999
也是允许的输出。


6
投票

编译器错误的第一条规则:这可能不是编译器错误,而是您的误解。在同一语句中使用后缀和前缀运算符会导致未定义的行为。尝试使用

-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

-3
投票

你的代码是为什么在一些书中评论有经验的程序员不喜欢 that(++,--) 运算符重载的一个例子,甚至其他语言(ruby)还没有实现 ++ 或 --.

© www.soinside.com 2019 - 2024. All rights reserved.