I understand that this is undefined behavior:
int i = 0;
int a[4];
a[i] = i++; //<--- UB here
because the order of evaluation of i
for the left hand side and the right hand side are undefined (the ;
is the only sequence point).
Taking that reasoning a step further it seems to me that this would be unspecified behavior:
int i = 0;
int foo(){
return i++;
}
int main(){
int a[4];
a[i] = foo();
return 0;
}
Even though there are a few sequence points on the right hand side of the =
as far as I understand it is still unspecified whether f()
or a[i]
is evaluated first.
Are my assumptions correct? Do I have to take painstaking care when I use a global or static variable on the left hand side of an assignment that the right hand does not under any circumstances modify it?
a[i] = foo();
Here, since C++17 the evaluation of foo()
is sequenced before a[i]
.
Before C++17, it was unspecified whether foo
or a[i]
is evaluated first (they are unsequenced relative to each other). That alone doesn't cause undefined behaviour, though. It is when there are two unsequenced accesses to the same scalar object, at least one of which is writing, where it does. That's why a[i] = i++;
is UB.
The difference between these two statements is that executions inside a called function are indeterminately sequenced with respect to other evaluations inside the calling function (rather than being unsequenced). Or in pre-C++11 terms, the call to foo()
introduces a sequence point.
This means there's a partial ordering between a[i]
and i++
inside foo
. As a result, either a[0]
or a[1]
will get set to 0, but the program is well defined.