[SOLVED] In a C function call, in which order are the parameters passed/evaluated?

Issue

I am running this C program:

#include<stdio.h>

void print(int* a, int* b, int* c, int* d, int* e)
{
    printf("%d %d %d %d %d\n", *a, *b, *c, *d, *e);
}

int main()
{
    static int a[] = {97, 98, 99, 100, 101, 102};
    int* ptr = a + 1;

    print(++ptr, ptr--, ptr, ptr++, ++ptr);
    ptr = a+ 1;
    printf("%d %d %d %d %d\n", *++ptr, *ptr--, *ptr, *ptr++, *++ptr);

    int i = 0;
    printf("%d %d %d", i++, ++i, ++i);
}

One compiler prints this as the output:

99 99 98 98 100
99 99 98 98 100
0 2 3

Another compiler prints this:

100 100 100 99 100
100 100 100 99 99
2 3 3

Now, I am confused what is happening. What is the correct order?

Solution

Evaluation of function calls is specified in the 2018 version of the C standard clause 6.5.2.2, but this clause does not specify the order of evaluation of the arguments. Any order is permitted.

As a consequence of this, the behavior of print(++ptr, ptr--, ptr, ptr++, ++ptr); is not defined by the C standard. 6.5 2 says:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings.

ptr is a scalar object. (C 2018 6.2.5 21 says “Arithmetic types and pointer types are collectively called scalar types.”) ++ptr and ptr-- change ptr by a side effect. (The “main” effect of ++ptr is to evaluate to the incremented value of ptr. Its side effect is to update the stored value of ptr.) Since there is no ordering imposed on these side effects, the conditions of 6.5 2 are satisfied, so it applies, and the behavior is not defined by the C standard.

Note that this does not just mean that ptr may be updated in whatever order the compiler happens to choose. When the C standard says behavior is undefined, that means it does not impose any requirements on the behavior at all. Two updates to the object might conflict, updating different bytes of it in different orders, producing a value that is impossible to obtain by separate consecutive updates in any order. (This could happen, for example, when a compiler implements four-byte pointers using two-byte updates in a primitive machine.) Or the optimizer in the compiler could recognize the code does not have defined behavior and remove it completely from the program or replace it by other code. (For example, if the program contains a branch between two sequences of code, and the optimizer recognizes one branch would have undefined behavior in a particular circumstances, it can omit the code that evaluates the choice and use only the code on the other branch.)

Answered By – Eric Postpischil

Answer Checked By – Mildred Charles (BugsFixing Admin)

Leave a Reply

Your email address will not be published.