As others have pointed out, the behavior is undefined:
6.5 Expressions
...
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.72) Furthermore, the prior value
shall be read only to determine the value to be stored.73)
...
72) A floating-point status flag is not an object and can be set more than once within an expression.
73) This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a[i] = i;
The expression i = ++i % 3
attempts to modify the value contained in i
twice before the next sequence point (in this case, the ;
ending the statement), once by evaluating ++i
, and once by evaluating the larger assignment expression.
Now, why would this be a problem? After all, C# and Java can handle these expressions just fine.
The problem is that, with few exceptions, C doesn't guarantee that operands in an expression are evaluated in any particular order, or that the side effects of an expression will be applied immediately after the expression is evaluated (unlike C# and Java, which do make those guarantees). For example, the expression ++i
has a result (i
+ 1) and a side effect (increment the value stored in i
); however, the side effect can be deferred until the larger expression has been evaluated. IOW, the following sequence of actions is allowed:
t0 = i + 1
t1 = t0 % 3
i = t1
i = i + 1
Oopsie. Not what we wanted.
This was a deliberate design decision; the idea is that it allows compilers to reorder evaluations in an optimal manner (by taking advantage of a value that's already in a register, say). The downside is that certain combinations of expressions will have unpredictable results.