Incrementing a volatile variable in C
From the draft C++ standard section 5.3.2
[expr.pre.incr] says:
If x is not of type bool, the expression ++x is equivalent to x+=1
and 5.17
[expr.ass] says:
The behavior of an expression of the form E1 op = E2 is equivalent to E1 = E1 op E2 except that E1 is evaluated only once.
So ++x
and x += 1
are equivalent.
Now the one case where x += 1
differs from x = x + 1
is that E1
is only evaluated once. In this particular case it does not matter but we can come up with a case where it does:
#include <stdint.h>volatile uint64_t x = 0;volatile uint64_t y[2] = {0} ;void c(void){ y[x] = y[x] + 1;}
in this case the x
will be evaluated twice as opposed to this case:
void b(void){ y[x] += 1;}
and a godbolt session shows for b()
:
b(): # @b()movq x(%rip), %raxincq y(,%rax,8)retq
and for c()
:
c(): # @c()movq x(%rip), %raxmovq y(,%rax,8), %raxincq %raxmovq x(%rip), %rcxmovq %rax, y(,%rcx,8)retq
As far as I can tell this applies to C11 as well. From C11 section 6.5.3.1
Prefix increment and decrement operators:
The expression ++E is equivalent to (E+=1).
and from section 6.5.16.2
Compound assignment:
Acompound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once
In the abstract semantics, all three of these expressions do exactly the same thing. They access x
to retrieve its value, calculate the new value, then store the updated value back in x
. There is an access and a store. (The expressions also yield a value, which is being discarded).
Although x = x + 1
mentions x
twice, the left side x
isn't evaluated. That is to say, not completely: its value is not computed. It is evaluated only to the extent of determining the location where the assigned value will go.
So there is possibly a double evaluation of location here: the left side determines the location of x
and so does the right side. But determining location doesn't involve accessing the location itself.
For some kinds of expressions, determining the location does involve accessing values. For example:
a[i] = a[i] + 1;
This is quite different from
i = i + 1
because i
is only a secondary variable here whose value must be known in order to determine the storage location of a[i]
(and i
isn't itself even being incremented). If i
is volatile
, then the two abstract accesses to it in a[i] = a[i] + 1
must correspond to two actual accesses.