I was puzzled by the fact that CPPReference says that postincrement’s value evaluation is sequenced before its side-effect, but preincrement has no such guarantee.
I have now come up with an example where this matters but I am unsure if my analysis is correct.
As I understand it, these two programs differ in that the first contains UB, and the second does not:
#include <stddef.h>
#include <stdio.h>
int main(void) {
int arr[] = {0, 1, 2};
int i = 1;
int x = ++arr[arr[i]];
}
#include <stddef.h>
#include <stdio.h>
int main(void) {
int arr[] = {0, 1, 2};
int i = 1;
int x = arr[arr[i]]++;
}
My analysis of the expression ++arr[arr[i]]
is as follows:
i
is sequenced before value computation of arr[i]
arr[i]
is sequenced before value computation of arr[arr[i]]
arr[arr[i]]
is sequenced before value computation of ++arr[arr[i]]
++arr[arr[i]]
is unsequenced with respect to these.arr[arr[i]]
since it is not used.arr[arr[i]]
refers to the same scalar object as arr[i]
.++arr[arr[i]]
modifies the scalar object arr[arr[i]]
, but it is accessed unsequenced to that by arr[i]
.However, if we use postincrement instead, we introduce a new sequenced-before relation: The value computation of arr[arr[i]]++
is sequenced before its side-effect. Therefore, by transitivity, the side-effect is no longer unsequenced to arr[i]
.
However, I am unsure if this is accurate. In particular, I am not sure how exactly evaluation of post-/preincrement is defined. Does it perform a value computation of its operand? If it does, does this mean that ++*ptr
is UB while (*ptr)++
is not? If it does not, how is the value computation of the full expression performed—can any operator access the value of an lvalue expression without performing value computation on that expression?
The analysis is incorrect. In particular, it is not the case that the side-effect of ++arr[arr[i]]
is unsequenced to the other value computations. This is because the C standard (Note: I have only read a draft of the C standard, N3096) specifies that ++E
is equivalent to E += 1
:
[6.5.16]
The expression ++E is equivalent to (E+=1), where the value 1 is of the appropriate type.
Further, it specifies that assignment operators incl. augmented assignment operators have their constituent expressions’ value computations sequenced before their side effect:
[6.5.3.1]
The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands.