Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why code is implemented differently on changing order in the assignment statement?

Tags:

c++

recursion

I am calling the factorial function defined in the following manner by reference.

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

when I pass the value 5 it returns the value 1 as I'd expected. But when I define the factorial function in the following way it returns the factorial of 5 that is 120.

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

I conjectured that the expression is evaluated in linear order and when a function is invoked in the expression all the values of local variables and the component expressions that have been evaluated so far in the original expression are stored and when the function returns control back to the caller these values that are retained are used in computation of the expression and not their modified values.

Is my hypothesis correct? Kindly enlighten me.

like image 458
Siddharth Joshi Avatar asked Dec 24 '22 07:12

Siddharth Joshi


1 Answers

I conjectured that the expression is evaluated in linear order [...]

Yes and no. The order of evaluation is typically done in linear order (either first to last or last to first), but is unspecified. When you write factorial(n)*(n+1), the compiler is allowed to evaluate (n+1) first or factorial(n) first. Different compilers will do it differently. Moreover, different versions of the same compiler could even change orderings, so that's not something you should ever rely on. The standardese is in [intro.execution]:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.

(The exceptions are things like &&, ||, ,, and ?:)

In this case, you can easily avoid relying on the order dependency completely by just removing the reference:

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

Now factorial(n-1) * n and n * factorial(n-1), regardless of which order they're evaluated in, both work and give you the same correct answer. This also has the added benefit that nobody would expect factorial to actually modify its argument anyway:

int i = 6;
int f = factorial(i);
// now i is 1??
like image 78
Barry Avatar answered Dec 28 '22 09:12

Barry