通过引用和返回的C ++函数调用不是预期的。

时间:2013-05-28 15:13:37

标签: c++

我写了一个简单的函数如下......

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

我不知道究竟是否有人可以解释。

3 个答案:

答案 0 :(得分:5)

评估表达式的结果是未指定,因为xfun(&x, &y)的评估是不确定的。

请注意,允许表达式评估产生未指定的结果 not 意味着您的程序具有未定义的行为;相反,它意味着你不能告诉通过评估该表达式将产生一组有限的可能结果

解释标准在这里可能非常棘手(实际上,在原始答案中我用错误的方式解释它 - 感谢Jerry Coffin注意到)。

要了解为什么这可能很棘手,让我们考虑C ++ 11标准第1.9 / 15段的第一部分:

  

除非另有说明,否则评估各个运算符的操作数和个体的子表达式   表达式没有排序。 [...]一个操作数的值计算   在运算符结果的值计算之前对运算符进行排序。 如果对标量有副作用   对于相对于同一标量对象的另一个副作用或值计算,对象未被排序   使用相同标量对象的值,行为未定义

例如,给定一个表达式,其中包括计算两个子表达式的总和,例如(考虑operator +这里没有重载):

e1 + e2

如果e1的评估使用某个标量s的值,并且e2的评估对该值有副作用,则行为未定义。例如,评估:

(i + i++)

由于上面引用的句子而给出了未定义的行为。

但是,你的情况有所不同。后面的同一段落指定:

  

调用函数时(无论函数是否为内联函数),[...]调用函数中的每个求值(包括其他函数调用)都没有特别说明   在执行被调用函数体之前或之后测序不确定地排序   关于被调用函数的执行。

这意味着如果要评估的表达式如下所示:

i + f(i)

f(i)增加i,行为不再是未定义的,只有未指定。换句话说,允许您的编译器以任何顺序评估if(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。你的代码也会发生同样的事情。