The specific rule in the C standard is C 2018 6.5 2:
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.
The first sentence is the critical one here. First, consider v[i] = i++;
. Here, the i
in v[i]
computes the value of i
, and the i++
both computes the value of i
and increments the stored value of i
. Computing the value of i
is a value computation of i
. Incrementing the stored value of i
is a side effect. To determine whether the behavior of v[i] = i++;
is undefined, we ask whether the side effect is unsequenced relative to any other side effect on i
or to a value computation on i
.
There is no other side effect on i
, so it is not unsequenced relative to any other side effect.
There is a value computation in i++
, but the side effect and this value computation are sequenced by the specification of the postfix ++
operator. C 2018 6.5.2.4 2 says:
… The value computation of the result is sequenced before the side effect of updating the stored value of the operand…
So we know the computation of the value of i
in i++
is sequenced before the side effect of incrementing the stored value.
Now we consider the value computation of the i
in v[i]
. The ++
specification does not tell us about this, so let’s consider the assignment operator, =
. The specification of assignment does say something about sequencing, in C 2018 6.5.16 3:
… The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.
The first sentence tells us the update of v[i]
is sequenced after the value computations of the left and right operands. But it does not tell us anything about the side effect in ++
relative to the value computation of i
in v[i]
.
Therefore, the value computation of i
in v[i]
is unsequenced relative to the side effect on i
in i++
, so the behavior of the statement is not defined by the C standard.
In a[i] = y++;
we have:
- A value computation on
i
in a[i]
.
- A value computation on
y
in y++
.
- An update of the stored value of
y
in y++
.
- A value computation on
a
in a[i]
.
- An update of the stored value of
a[i]
in a[i] = …
.
The only object that is updated twice or that is both updated and evaluated is y
, and we know from above that the value computation on y
in y++
is sequenced before the update of y
. So this statement does not contain any side effect that is unsequenced relative to another side effect or value cmputation on the same object. So its behavior is not undefined by the rule in C 2018 6.5 2.
Similarly, in a[i++] = y;
, we have:
- A value computation on
i
in a[i++]
.
- An update of the stored value of
i
in i++
.
- A value computation on
y
.
- A value computation on
a
in a[i]
.
- An update of the stored value of
a[i]
in a[i++] = …
.
Again, there is only one object with two operations on it, and those operations are sequenced. The behavior is not undefined by the rule in C 2018 6.5 2.
Note
In the above, we assume neither a
nor v
is a pointer such that a[i]
or v[i]
would be i
or y
. If instead we consider this code:
int y = 3;
int *a = &y;
int i = 0;
a[i] = y++;
Then the behavior is undefined because a[i]
is y
, so the code updates y
twice, once for the assignment a[i] = …
and once for y++
, and these updates are unsequenced. The specification of assignment says the update to the left operand is sequenced after the value computation of the result (which is the value of the right side of the assignment), but the increment for ++
is a side effect, not part of the value computation. So the two updates are unsequenced, and the behavior is not defined by the C standard.