Modifying any object more than once between two sequence points
suggest changeint i = 42;
i = i++; /* Assignment changes variable, post-increment as well */
int a = i++ + i--;
Code like this often leads to speculations about the “resulting value” of i
. Rather than specifying an outcome, however, the C standards specify that evaluating such an expression produces undefined behavior. Prior to C2011, the standard formalized these rules in terms of so-called sequence points:
Between the previous and next sequence point a scalar 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.
(C99 standard, section 6.5, paragraph 2)
That scheme proved to be a little too coarse, resulting in some expressions exhibiting undefined behavior with respect to C99 that plausibly should not do. C2011 retains sequence points, but introduces a more nuanced approach to this area based on sequencing and a relationship it calls “sequenced before”:
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.
(C2011 standard, section 6.5, paragraph 2)
The full details of the “sequenced before” relation are too long to describe here, but they supplement sequence points rather than supplanting them, so they have the effect of defining behavior for some evaluations whose behavior previously was undefined. In particular, if there is a sequence point between two evaluations, then the one before the sequence point is “sequenced before” the one after.
The following example has well-defined behaviour:
int i = 42;
i = (i++, i+42); /* The comma-operator creates a sequence point */
The following example has undefined behaviour:
int i = 42;
printf("%d %d\n", i++, i++); /* commas as separator of function arguments are not comma-operators */
As with any form of undefined behavior, observing the actual behavior of evaluating expressions that violate the sequencing rules is not informative, except in a retrospective sense. The language standard provides no basis for expecting such observations to be predictive even of the future behavior of the same program.