The C99 Standard says in $6.5.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. Furthermore, the prior value shall be read only to determine the value to be stored.
(emphasis by me)
It goes on to note, that the following example is valid (which seems obvious at first)
a[i] = i;
While it does not explicitly state what a
and i
are.
Although I believe it does not, I'd like to know whether this example covers the following case:
int i = 0, *a = &i;
a[i] = i;
This will not change the value of i
, but access the value of i
to determine the address where to put the value. Or is it irrelevant that we assign a value to i
which is already stored in i
? Please shed some light.
Bonus question; What about a[i]++
or a[i] = 1
?
Reading an object's value to determine where to store it does not count as "determining the value to be stored". This means that the only point of contention can be whether or not we are "modifying" the object
i
: if we are, it is undefined; if we are not, it is OK.Does storing the value
0
into an object which already contains the value0
count as "modifying the stored value"? By the plain English definition of "modify" I would have to say not; leaving something unchanged is the opposite of modifying it.It is, however, clear that this would be undefined behaviour:
Here there can be no doubt that the stored value is read for a purpose other than determining the value to be stored (the value to be stored is a constant), and that the value of
i
is modified.The first sentence:
is clear enough. The language doesn't impose an order of evaluation on subexpressions unless there's a sequence point between them, and rather than requiring some unspecified order of evaluation, it says that modifying an object twice produces undefined behavior. This allows aggressive optimization while still making it possible to write code that follows the rules.
The next sentence:
does seem unintuitive at first (and second) glance; why should the purpose for which a value is read affect whether an expression has defined behavior?
But what it reflects is that if a subexpression B depends on the result of a subexpression A, then A must be evaluated before B can be evaluated. The C90 and C99 standards do not state this explicitly.
A clearer violation of that sentence, given in an example in the footnote, is:
Assuming that
a
is a declared array object andi
is a declared integer object (no pointer or macro trickery), no object is modified more than once, so it doesn't violate the first sentence. But the evaluation ofi++
on the LHS determines which object is to be modified, and the evaluation ofi
on the RHS determines the value to be stored in that object -- and the relative order of the read operation on the RHS and the write operation on the LHS is not defined. Again, the language could have required the subexpressions to be evaluated in some unspecified order, but instead it left the entire behavior undefined, to permit more aggressive optimization.In your example:
the previous value of
i
is read both to determine the value to be stored and to determine which object it's going to be stored in. Sincea[i]
refers toi
(but only becausei==0
), modifying the value ofi
would change the object to which the lvaluea[i]
refers. It happens in this case that the value stored ini
is the same as the value that was already stored there (0
), but the standard doesn't make an exception for stores that happen to store the same value. I believe the behavior is undefined. (Of course the example in the standard wasn't intended to cover this case; it implicitly assumes thata
is a declared array object unrelated toi
.)As for the example that the standard says is allowed:
one could interpret the standard to say that it's undefined. But I think that the second sentence, referring to "the prior value", applies only to the value of an object that's modified by the expression.
i
is never modified by the expression, so there's no conflict. The value ofi
is used both to determine the object to be modified by the assignment, and the value to be stored there, but that's ok, since the value ofi
itself never changes. The value ofi
isn't "the prior value", it's just the value.The C11 standard has a new model for this kind of expression evaluation -- or rather, it expresses the same model in different words. Rather than "sequence points", it talks about side effects being sequenced before or after each other, or unsequenced relative to each other. It makes explicit the idea that if a subexpression B depends on the result of a subexpression A, then A must be evaluated before B can be evaluated.
In the N1570 draft, section 6.5 says: