提问人:pravs 提问时间:5/28/2012 最后编辑:R Sahupravs 更新时间:10/24/2017 访问量:12945
cout << a++ << a;的正确答案是什么?
What is the correct answer for cout << a++ << a;?
问:
在最近的一次采访中,有一个客观类型的问题。
int a = 0;
cout << a++ << a;
答案:
A. 10
B. 01
C. 未定义的行为
我回答了选项 b,即输出将是“01”。
但后来令我惊讶的是,面试官告诉我,正确答案是选项c:未定义。
现在,我确实知道 C++ 中序列点的概念。对于以下语句,该行为未定义:
int i = 0;
i += i++ + i++;
但根据我对语句的理解,会被调用两次,第一次是 with 和 后来。cout << a++ << a
ostream.operator<<()
ostream.operator<<(a++)
ostream.operator<<(a)
我还在 VS2010 编译器上检查了结果,其输出也是“01”。
答:
从技术上讲,总的来说,这是未定义的行为。
但是,答案有两个重要方面。
代码语句:
std::cout << a++ << a;
评估如下:
std::operator<<(std::operator<<(std::cout, a++), a);
该标准没有定义函数参数的计算顺序。
所以要么:
std::operator<<(std::cout, a++)
首先评估或a
首先评估或- 它可以是任何实现定义的顺序。
根据标准,此订单未指定[参考文献 1]。
[参考文献 1]C++03 5.2.2 函数调用
第 8 段
参数的计算顺序未指定。参数表达式计算的所有副作用都会在函数输入之前生效。后缀表达式和参数表达式列表的计算顺序未指定。
此外,在函数的参数计算之间没有序列点,但只有在计算所有参数之后才存在序列点[参考文献 2]。
[参考文献 2]C++03 1.9 程序执行 [intro.execution]: 第 17 段:
调用函数时(无论函数是否内联),在函数体中执行任何表达式或语句之前,在计算所有函数参数(如果有)之后都会有一个序列点。
请注意,这里的值被多次访问,没有中间的序列点,关于这一点,标准说:c
[参考文献 3]C++03 5 表达式 [expr]: 第 4 段:
....
在上一个和下一个序列点之间,标量对象的存储值最多应通过表达式的计算进行一次修改。此外,应仅访问先前的值以确定要存储的值。对于完整子表达式的每个允许排序,应满足本款的要求 表达;否则,行为是未定义的。
代码在不干预序列点的情况下多次修改,并且不会访问它以确定存储对象的值。这显然违反了上述条款,因此标准规定的结果是未定义行为[参考文献 3]。c
评论
c
你可以想到:
cout << a++ << a;
奥斯特
std::operator<<(std::operator<<(std::cout, a++), a);
C++ 保证先前评估的所有副作用都将在序列点执行。函数参数求值之间没有序列点,这意味着可以在参数之前或之后求值参数。所以上面的结果是不确定的。a
std::operator<<(std::cout, a++)
C++17 更新
在 C++17 中,规则已更新。特别:
在移位运算符表达式 和 中,的每个值计算和副作用都先于每个值计算和副作用进行排序。
E1<<E2
E1>>E2
E1
E2
这意味着它需要代码来生成 result ,它输出 .b
01
有关详细信息P0145R3请参阅优化惯用C++的表达式计算顺序。
评论
c
int
operator<<
operator<<
So the result of the above is undefined.
您的解释仅适用于未指定,不适用于未定义。JamesKanze在他的回答中解释了为什么它更的不确定。
序列点仅定义部分排序。就您而言,您有 (一旦完成过载解决):
std::cout.operator<<( a++ ).operator<<( a );
在 和第一次调用之间有一个序列点,并且在
第二个和第二个调用,但有
在 和 之间没有序列点;唯一的订购
约束条件需要充分评估(包括副作用)
在第一次调用之前,第二次调用 完全
在第二次调用 之前进行评估。(还有
causual 排序约束:第二次调用 cannot
在第一个之前,因为它需要第一个的结果作为
参数。§5/4 (C++03) 规定:a++
std::ostream::operator<<
a
std::ostream::operator<<
a++
a
a++
operator<<
a
operator<<
operator<<
除非另有说明,否则 计算单个运算符的操作数和子表达式 单个表情,以及副作用发生的顺序, 未指定。在上一个序列点和下一个序列点之间有一个标量 对象的存储值最多应由 表达式的计算。此外,先前的值应为 仅访问以确定要存储的值。要求 对于每个允许的顺序,应满足本款要求 完整表达式的子表达式;否则,该行为是 定义。
表达式的允许顺序之一是 , , first
调用 ,第二次调用 ;这将修改
存储值 (),并访问它,而不是确定
新值(第二个),行为未定义。a++
a
operator<<
operator<<
a
a++
a
评论
c
++
int
c
foo(foo(bar(c)), c)
c
c
c
c++
c
c++
f(c++)
c
f
&&
||
?:
正确答案是质疑问题。这种说法是不可接受的,因为读者看不到明确的答案。另一种看待它的方法是,我们引入了副作用 (c++),使语句更难解释。简洁的代码很棒,只要它的含义很清楚。
评论
上一个:将共享指针作为参数传递
评论
10
01
00
c++
c