Parameter evaluation order before a function calling in C [duplicate]
Can it be assumed a evaluation order of the function parameters when calling it in C ? According to the following program, it seems that there is not a particular order when I executed it.
#include <stdio.h>
int main()
{
int a[] = {1, 2, 3};
int * pa;
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
/* Result: a[0] = 3 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(pa),*(++pa));
/* Result: a[0] = 2 a[1] = 2 a[2] = 2 */
pa = &a[0];
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa++),*(++pa), *(pa));
/* a[0] = 2 a[1] = 2 a[2] = 1 */
}
No, function parameters are not evaluated in a defined order in C.
See Martin York's answers to What are all the common undefined behaviour that c++ programmer should know about?.
Order of evaluation of function arguments is unspecified, from C99 §6.5.2.2p10:
The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.
Similar wording exists in C89.
Additionally, you are modifying pa
multiple times without intervening sequence points which invokes undefined behavior (the comma operator introduces a sequence point but the commas delimiting the function arguments do not). If you turn up the warnings on your compiler it should warn you about this:
$ gcc -Wall -W -ansi -pedantic test.c -o test
test.c: In function ‘main’:
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:9: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:13: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:17: warning: operation on ‘pa’ may be undefined
test.c:20: warning: control reaches end of non-void function
Just to add some experiences.
The following code:
int i=1;
printf("%d %d %d\n", i++, i++, i);
results in
2 1 3
- using g++ 4.2.1 on Linux.i6861 2 3
- using SunStudio C++ 5.9 on Linux.i6862 1 3
- using g++ 4.2.1 on SunOS.x86pc1 2 3
- using SunStudio C++ 5.9 on SunOS.x86pc1 2 3
- using g++ 4.2.1 on SunOS.sun4u1 2 3
- using SunStudio C++ 5.9 on SunOS.sun4u
Can it be assumed a evaluation order of the function parameters when calling it in C ?
No, it can not be assumed if, it is unspecified behavior, the draft C99 standard in section6.5
paragraph 3
says:
The grouping of operators and operands is indicated by the syntax.74) Except as specified later (for the function-call (), &&, ||, ?:, and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
It also says except as specified later and specifically sites function-call ()
, so we see that later on the draft standard in section 6.5.2.2
Function calls paragraph 10
says:
The order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.
This program also exhibits undefined behavior since you are modifying pa
more than once between sequence points. From draft standard section 6.5
paragraph 2
:
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.
it cites the following code examples as being undefined:
i = ++i + 1;
a[i++] = i;
Important to note that although the comma operator does introduce sequence points, the comma used in function calls is a separator and not the comma operator
. If we look at section 6.5.17
Comma operator paragraph 2
says:
The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation.
but paragraph 3
says:
EXAMPLE As indicated by the syntax, the comma operator (as described in this subclause) cannot appear in contexts where a comma is used to separate items in a list (such as arguments to functions or lists of initializers).
Without knowing this, having warnings turned on with gcc
using at least -Wall
would have provided a message similar to:
warning: operation on 'pa' may be undefined [-Wsequence-point]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
^
and by default clang
will warn with a message similar to:
warning: unsequenced modification and access to 'pa' [-Wunsequenced]
printf("a[0] = %d\ta[1] = %d\ta[2] = %d\n",*(pa), *(pa++),*(++pa));
~ ^
In general it is important to understand how to use your tools in the most effective way, getting to know the flags available for warnings is important, for gcc
you can find that information here. Some flags that are useful and will save you a lot of trouble in the long run and are common to both gcc
and clang
are -Wextra -Wconversion -pedantic
. For clang
understanding -fsanitize can be very helpful. For example -fsanitize=undefined
will catch many instances of undefined behavior at runtime.