From the C++ (C++11) standard, §1.9.15 which discusses ordering of evaluation, is the following code example:
void g(int i, int* v) {
i = v[i++]; // the beha
There two rules.
The first rule is about multiple writes which give rise to a "write-write hazard": the same object cannot be modified more than once between two sequence points.
The second rule is about "read-write hazards". It is this: if an object is modified in an expression, and also accessed, then all accesses to its value must be for the purpose of computing the new value.
Expressions like i++ + i++
and your expression i = v[i++]
violate the first rule. They modify an object twice.
An expression like i + i++
violates the second rule. The subexpression i
on the left observes the value of a modified object, without being involved in the calculation of its new value.
So, i = v[i++]
violates a different rule (bad write-write) from i + i++
(bad read-write).
The rules are too simplistic, which gives rise to classes of puzzling expressions. Consider this:
p = p->next = q
This appears to have a sane data flow dependency that is free of hazards: the assignment p =
cannot take place until the new value is known. The new value is the result of p->next = q
. The the value q
should not "race ahead" and get inside p
, such that p->next
is affected.
Yet, this expression breaks the second rule: p
is modified, and also used for a purpose not related to computing its new value, namely determining the storage location where the value of q
is placed!
So, perversely, compilers are allowed to partially evaluate p->next = q
to determine that the result is q
, and store that into p
, and then go back and complete the p->next =
assignment. Or so it would seem.
A key issue here is, what is the value of an assignment expression? The C standard says that the value of an assignment expression is that of the lvalue, after the assignment. But that is ambiguous: it could be interpreted as meaning "the value which the lvalue will have, once the assignment takes place" or as "the value which can be observed in the lvalue after the assignment has taken place". In C++ this is made clear by the wording "[i]n all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.", so p = p->next = q
appears to be valid C++, but dubious C.