Here\'s an example snippet:
int i = 4,b;
b = foo(i++) + foo(i++);
I\'m pretty certain it\'s not undefined, because the
The behavior is undefined.
b = foo(i++) + foo(i++);
As you say, there's a sequence point between the evaluation of the first i++
and the call to foo
, and likewise between the evaluation of the second i++
and the call foo
. But there isn't (necessarily) a sequence point between the two evaluations of i++
, or more specifically between their side effects (modifying i
).
Quoting the N1570 draft of the 2011 ISO C standard, section 6.5.2.2p10:
There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.
The second sentence is significant here: the two evaluations of i++
are "indeterminately
sequenced" with respect to the two function calls, meaning that they can occur either before or after the calls to foo
. (They're not unsequenced, though; each of them occurs either before or after the calls, but it's unspecified which.)
And 6.5p2 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.
Putting this together, a conforming implementation could evaluate the expression in this order:
i++
and save the value somewhere.i++
and save the value somewhere.foo
, passing the first saved value as an argument.foo
, passing the second saved value as an argument.b
.There is no sequence point between steps 1 and 2, both of which modify i
, so the behavior is undefined.
(That's actually a slight oversimplification; the side effect of modifying i
can be separated from the determination of the result of i++
.
Bottom line: We know that
b = i++ + i++;
has undefined behavior, for reasons that have been explained repeatedly. Wrapping the i++
subexpressions in function calls does add some sequence points, but those sequence points don't separate the two evaluations of i++
and therefore don't cause the behavior to become well defined.
Even bottommer line: Please don't write code like that. Even if the behavior were well defined, it would be more difficult than it's worth to prove it and to determine what the behavior should be.