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:
- There are these sequenced-before relations:
- Value computation of
iis sequenced before value computation ofarr[i] - Value computation of
arr[i]is sequenced before value computation ofarr[arr[i]] - Value computation of
arr[arr[i]]is sequenced before value computation of++arr[arr[i]]
- Value computation of
- The side-effect of
++arr[arr[i]]is unsequenced with respect to these. - The compiler may choose any order satisfying these relations and may delete the value computation of
arr[arr[i]]since it is not used. - In any possible ordering, the value computation of
arr[arr[i]]refers to the same scalar object asarr[i]. - The side effect of
++arr[arr[i]]modifies the scalar objectarr[arr[i]], but it is accessed unsequenced to that byarr[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++Eis equivalent toE += 1:Further, it specifies that assignment operators incl. augmented assignment operators have their constituent expressions’ value computations sequenced before their side effect: