When you write a function call such as printf("%d %d %d %d %d",a++,a,++a,a++,++a);
, it turns into a set of operations to be performed:
- The first argument evaluates to a pointer to the string.
- The second argument results in two operations: Produce the value of
a
. Set a
to the value of a
incremented by one.
- The third argument evaluates to the value of
a
.
- The fourth argument results in two operations: Produce one more than the value of
a
. Set a
to the value of a
increment by one.
- The fifth argument results in two operations: Produce the value of
a
. Set a
to the value of a
incremented by one.
- The sixth argument results in two operations: Produce one more than the value of
a
. Set a
to the value of a
increment by one.
- The function call passes the arguments to the function and calls it.
The C implementation has to perform all these operations. However, the C standard does not fully specify the order in which they are performed. Per C 2011 (N1570) 6.5.2.3 10, there is a sequence point after the evaluations of the arguments and before the call. This means that the operations resulting from the arguments themselves must be performed before the call is performed.
However, the C standard does not say in what order the arguments are evaluated. It does not even say that the two operations resulting from a single expression (as in a++
) have to be performed together. The C implementation is allowed to prepare the value of a
, then evaluate some other arguments, then later set a
to the value of a
incremented by one.
This means that your function call might be using the value of a
and setting the value of a
, multiple times, in any number of different orders. That is a problem because it violates a rule in 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.…
In this passage, “unsequenced” means that two operations do not have a definite order set by the C standard: If a C implementation is allowed to perform two operations in either order, then they are unsequenced. Your printf
call contains several unsequenced operations that have side effects on a
(setting a
is a side effect of a++
) and that compute a value (simply using a
to pass an argument “computes” its value).
Therefore, your printf
call violates the rules of the C standard, and the resulting behavior is undefined.
Every normal expression you write should modify any object only once. Never use a++
twice in the same expression. Additionally, if you modify an object, you cannot separately use it. If you have a++
one place in an expression, you cannot have a
separately in another place.
Some special operators have exceptions. The &&
and ||
operators are required to evaluate their left operands first. This causes subexpressions on the left to be sequenced before subexpressions on the right, and that avoids violating the rule in C 2011 6.5 2. Therefore a++ && a++
is legal. The comma operator also evaluates subexpressions on the left first, so a++, ++a, a
is legal. However, arguments to a function call are a list of arguments, not a use of the comma operator.