我写了一个简单的函数如下......
int fun(int *i, int *j)
{
*i += 3;
*j += *i;
return (*i + *i);
}
在主函数中我调用了函数如下......
int main()
{
int x = 3, y = 2, a, b;
a = x + y + fun(&x, &y) + x;
b = y + fun(&x, &y) + y;
cout<<" a = " << a << "\n";
cout<<" b = " << b << "\n";
}
现在问题是输出不是预期的
运行后这是输出
a=23
b=52
我在期待
a=23
b=46
我不知道究竟是否有人可以解释。
答案 0 :(得分:5)
评估表达式的结果是未指定,因为x
和fun(&x, &y)
的评估是不确定的。
请注意,允许表达式评估产生未指定的结果 not 意味着您的程序具有未定义的行为;相反,它意味着你不能告诉通过评估该表达式将产生一组有限的可能结果。
解释标准在这里可能非常棘手(实际上,在原始答案中我用错误的方式解释它 - 感谢Jerry Coffin注意到)。
要了解为什么这可能很棘手,让我们考虑C ++ 11标准第1.9 / 15段的第一部分:
除非另有说明,否则评估各个运算符的操作数和个体的子表达式 表达式没有排序。 [...]一个操作数的值计算 在运算符结果的值计算之前对运算符进行排序。 如果对标量有副作用 对于相对于同一标量对象的另一个副作用或值计算,对象未被排序 使用相同标量对象的值,行为未定义。
例如,给定一个表达式,其中包括计算两个子表达式的总和,例如(考虑operator +
这里没有重载):
e1 + e2
如果e1
的评估使用某个标量s
的值,并且e2
的评估对该值有副作用,则行为未定义。例如,评估:
(i + i++)
由于上面引用的句子而给出了未定义的行为。
但是,你的情况有所不同。后面的同一段落指定:
调用函数时(无论函数是否为内联函数),[...]调用函数中的每个求值(包括其他函数调用)都没有特别说明 在执行被调用函数体之前或之后测序不确定地排序 关于被调用函数的执行。
这意味着如果要评估的表达式如下所示:
i + f(i)
f(i)
增加i
,行为不再是未定义的,只有未指定。换句话说,允许您的编译器以任何顺序评估i
和f(i)
,但可能的结果只是这些可能评估之一的结果。
如果行为未定义,另一方面,可能的结果可能是任何(从崩溃到行为与预期一致,在控制台上打印奇怪的消息,{{3 }})。
答案 1 :(得分:1)
您的代码有未指定的结果。
未指定表达式中子表达式的求值顺序。因此,在x + y + fun(&x, &y) + x;
中,函数调用之外的x
值可以是函数调用之前或函数调用之后的x
值。由于你在其外的两个不同的地方有x
,一个可以来自函数调用之前,一个来自之后 - 或者两者都可以来自之前或两者都可以来自之后。
在进入函数之前和离开函数之前有一个序列点,因此在函数外部使用x
的值和在函数内修改其值之间存在一个序列点。因此,您的行为已定义,但未指定。
从C ++ 11开始,“序列点”术语已从标准中删除,有利于术语,如“之前排序”,“之后排序”或“未遵循”。在此过程中,已在少数位置指定了以前不存在的顺序。我不相信任何先前指定的排序(例如进入和退出函数的序列点)已被删除。换句话说,除非我完全弄错,否则在这种情况下不会改变情况。
答案 2 :(得分:1)
考虑下面给出的代码,
x=10;
val=x+f(&x);
void f(int *a)
{
*a=*a+10;
}
C ++标准没有定义操作数的左侧是首先评估还是右侧评估。因此,如果首先评估左侧,则变量val
中的值将为30,如果首先评估右侧,则值将为40。你的代码也会发生同样的事情。