An expression in C, including a subexpression of another expression, may have two effects:
- A main effect: It produces a value to be further used in the containing expression.
- A side effect: It modifies an object or file or accesses a volatile object.
In general, the C standard does not say when the side effect occurs. It does not necessarily occur at the same time that the main effect is evaluated.
In x = y == y++;
, y++
has the side effect of modifying y
. However, y
is also used as the left operand of ==
. Because the C standard does not say when the side effect will occur, it may occur before, during, or after using the value of y
for the left operand.
A rule in the C standard says that if using the value of an object is not sequenced (specified to occur before or after) relative to a modification of that object, the behavior is undefined. Also, if two modifications are unsequenced relative to each other, the behavior is undefined.
An original motivation for this rule is that increment y
for y++
could require multiple steps. In a computer with only 16-bit arithmetic, a C implementation might support a 32-bit int
by using multiple instructions to get the low 16 bits of y
, add 1 to them, remember the carry, store the resulting 16 bits, get the high 16 bits of y
, add the carry, and store the resulting bits. If some other code is separately trying to get the value of y
, it might get the low 16 bits after the 1 has been added but get the high 16 bits before the carry has been added, and the result could be a value of y
that is neither the value before the add (e.g., 0x1ffff) nor the value after the add (e.g.&, 0x20000) but a mix (0x10000). You could say an implementation ought to track operations on objects and keep them separate so this interleaving does not occur. However, that can impose a burden on compilers and interfere with optimization.