Search code examples
cparameter-passingfunction-calls

(C) Can someone explain to me why the code returns what it does?


So I was talking a lynda course on learning the C language and this example was shown and barely explained so I was unable to understand why there results were what they were.Keep in mind the code isn't supposed to be correct, I'm just supposed to understand what happens.

#include <stdio.h>

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )

int increment() {
    static int i = 42;
    i += 5;
    printf("increment returns %d\n", i);
    return i;
}

int main( int argc, char ** argv ) {
    int x = 50;
    printf("max of %d and %d is %d\n", x,increment(), MAX(x, increment()));
    printf("max of %d and %d is %d\n", x,increment(), MAX(x, increment()));
    return 0;
}

and the result is:

increment returns 47
increment returns 52
max of 50 and 52 is 50
increment returns 57
increment returns 62
increment returns 67
max of 50 and 67 is 62

Can someone explain to me why increment returns 47 because if a is int x and int x = 50 and b is 47 because it executes MAX(x, increment()) . If I'm not reading the code wrong it should print 50 because 50 is greater than 47.


Solution

  • There are three issues at play here.

    First is that your increment function changes state every time you call it - it will always return a different value for each call. Second is that function arguments are not guaranteed to be evaluated left-to-right. Third is that after macro expansion, your printf call looks like this:

    printf("max of %d and %d is %d\n", x,increment(), (x) > (increment()) ? (x) : (increment()));
    

    so it's possible for increment to be called 3 times.

    Based on your output, increment is being called in this order:

    printf("max of %d and %d is %d\n", x, increment(), (x) > (increment()) ? (x) : (increment()));
                                          ^                   ^
                                          |                   |
                                          |                   +---- increment returns 47
                                          +------------------------ increment returns 52
    

    That is, the expression (x) > (increment()) ? (x) : (increment()) is being evaluated first - increment() returns 47, which is not greater than x (50), so the result of the expression is 50.

    Sometime after that, the lone increment() expression is called, which returns 52.

    With the second printf call, we get

    printf("max of %d and %d is %d\n", x, increment(), (x) > (increment()) ? (x) : (increment()));
                                          ^                   ^                     ^
                                          |                   |                     +---- increment returns 62
                                          |                   +-------------------------- increment returns 57
                                          +---------------------------------------------- increment returns 67
    

    Again, (x) > (increment()) ? (x) : (increment()) is evaluated first. This time, increment() is called twice, returning 57 in the test condition, then 62 in the result.

    It's then called a third time when the expression increment() is evaluated.

    So...

    The best way to deal with this is to assign the result of increment to a temporary, and use that temporary in the printf calls:

    int tmp = increment();
    printf( "max of %d and %d is %d\n", x, tmp, MAX(x, tmp) );
    

    Most operators in C don't force left to right evaluation. The few that do are the logical && and || operators, the ?: ternary operator, and the , comma operator (which is not the same as the commas used in parameter lists for function calls).