我对代码的输出感到困惑。 这取决于我运行代码的编译器。为什么会这样?
#include <iostream>
using namespace std;
int f(int &n)
{
n--;
return n;
}
int main()
{
int n=10;
n=n-f(n);
cout<<n;
return 0;
}
使用g ++在Ubuntu终端上运行它,输出为1,而在Turbo C ++上运行它(我们在学校使用的编译器)将输出设为0。
答案 0 :(得分:4)
在C ++ 03中,修改变量并在同一表达式中使用其值,而没有介入的C ++ 03 序列点,未定义行为。
C ++03§5/ 4:“之间 和下一个序列点标量对象的评估值最多只能修改一次 一个表达。此外,只能访问先前值以确定要存储的值。 对于完整的子表达式的每个允许排序,应满足本段的要求 表达;否则行为未定义。
Undefined Behavior,UB,为编译器提供了优化的机会,因为它可以假设UB不会出现在有效的程序中。
但是,有了C ++的所有无数UB规则,很难推断出源代码。
在C ++ 11中,
C ++11§1.9/ 3
“鉴于任何两项评估 A 和 B ,如果
A 在 B 之前排序,然后A的执行应先于 B 的执行。如果 A 之前没有排序
B 和 B 在 A 之前未排序,然后 A 和 B < EM>未测序
。 [注意:执行未经检测的
评估可以重叠。 -end note ]当 A 时,评估 A 和 B
在 B 或 B 在 A 之前对其进行排序之前对其进行排序,但未指定哪个。
使用新的C ++ 11序列关系规则,相对于变量的使用,有问题的代码中函数的修改是不确定的,因此代码具有未指定的行为而不是未定义的行为,正如Eric {{M}在a comment to (the first version of) this answer中所指出的那样。基本上这意味着没有鼻子守护进程或其他可能的UB效应的危险,并且这种行为是合理的。这里的两个可能的行为是通过函数调用的修改在使用值之前完成,或者在使用值之后完成。
为什么这是未指明的行为:
C ++11§1.9/ 15:“调用函数中的每个评估(包括其他函数调用)都没有特别说明 在执行被调用函数体之前或之后对序列进行测序是不确定的 关于被调用函数的执行。
“未指明的行为”是指:
C ++11§1.3.25:“未指明的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现 [注意:不需要实现来记录发生的行为。可能的范围 行为通常由本国际标准划定。 -end note ]
为什么通过分配实施的修改不成问题:
C ++11§5.17/ 1“在所有情况下,分配都会在值之后排序 计算右和左操作数,并在赋值表达式的值计算之前。
这与C ++ 03完全不同。
正如这个答案的相当激烈的编辑所示,在Eric的评论之后,这种问题并不简单!我可以给出的主要建议是尽可能地让Say No™受到由细微或非常复杂的规则(语言的角落)支配的影响。简单的代码更有可能是正确的,而所谓的聪明的代码不太可能显着更快。