为什么在赋值语句中更改顺序时,代码的实现方式不同?

时间:2016-03-21 14:11:27

标签: c++ recursion

我通过引用调用以下方式定义的阶乘函数。

int factorial(int &n) {
    n--;
    if (n>0) return factorial(n)*(n+1);
    else return 1;
}

当我传递值5时,它返回值1,正如我预期的那样。但是当我以下面的方式定义阶乘函数时,它返回5的阶乘,即120.

int factorial(int &n) {
     n--;
     if (n>0) return (n+1)*factorial(n);
     else return 1;
}

我推测表达式是按线性顺序计算的,当在表达式中调用函数时,存储局部变量的所有值和到目前为止在原始表达式中已经计算过的组件表达式,并且当函数返回控制时返回给调用者这些保留的值用于计算表达式而不是它们的修改值。

我的假设是否正确?请赐教。

1 个答案:

答案 0 :(得分:10)

  

我推测表达式是按线性顺序评估的[...]

是和否。评估顺序通常以线性顺序(从第一个到最后一个或从最后一个到第一个)完成,但未指定。当您编写factorial(n)*(n+1)时,允许编译器先评估(n+1)或先评估factorial(n)。不同的编译器将采用不同的方式。而且,同一编译器的不同版本甚至可以改变排序,因此这不是你应该依赖的东西。标准位于[intro.execution]

  

除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的。 [...]如果标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值进行的值计算未被排序,并且它们不可能并发(1.10),< strong>行为未定义。

(例外情况包括&&||,?:

在这种情况下,只需删除引用即可轻松完全依赖订单依赖:

int factorial(int n) {
    if (n>0) return factorial(n-1)*(n);
    else return 1;
}

现在factorial(n-1) * nn * factorial(n-1),无论评估的顺序如何,都可以正常工作,并为您提供相同的正确答案。这也有额外的好处,没有人会期望factorial实际上修改它的论点:

int i = 6;
int f = factorial(i);
// now i is 1??