cfunctioncalling-convention

Difference between calling convention and order of evaluation of function arguments in C?


The following code yields different outputs on different compilers;

#include <stdio.h>

void fun(int x, int y, int z)  /* function definition */
{
    printf("%d %d %d \n", x, y, z);  /* 6 6 6 */
}

int main()
{
    int a = 3;
    fun(++a, ++a, ++a);  /* function call */
    return 0;
}

So far the code has yielded the outputs

on different compilers.

Does this behaviour have anything to do with order of evaluation of function arguments or could it be due to calling convention (cdecl)? Will changing to a convention (if that’s possible) change the output of the aforementioned code in any way?

The code snippet is from a book which was trying to introduce calling conventions in C. I’m new to C so please keep it as simple as possible or provide context.


Solution

  • A calling convention specifies what the state of the computer must be at the moment a function is called and at the moment a function returns. It says nothing about how the computer arrived at that state, and therefore it says nothing about the order in which function arguments must be evaluated.

    For example, a calling convention might say that, for a specific function interface, arguments 1, 2, 3, and 4 must be, respectively, in processor register A, processor register B, location X on the stack, and location Y on the stack. However, the calling routine could calculate argument 2 and put it in register B, then calculate argument 3 and put it in location X, then calculate argument 4 and put it in location Y, and then calculate argument 1 and put it in register A. As long as all the arguments are put into their required locations by the time the function is called, the calling convention is satisfied.

    In fun(++a, ++a, ++a), neither a calling convention nor the C standard specify the order in which the arguments are evaluated. The compiler is free to generate instructions evaluating the arguments in any order. Further, the pre-increment operator consists of two operations: Incrementing the value of the operand and using the value of the operand. These operations are not tied together, so the three increments and the three uses of the values of a in fun(++a, ++a, ++a) may be rearranged into any order.

    The C standard says that when multiple modifications to a scalar object are unsequenced or a modification to a scalar object is not sequenced with a use of its value, the behavior of the program is not defined. So, the behavior of a program that executes fun(++a, ++a, ++a) is not defined by the C standard.