I was looking at the following code:
#include <stdint.h>
void foo(uint32_t *pVal)
{
uint32_t i = 8;
*pVal = i *= 10;
}
At the first glance it is clear that before returning from foo()
, *pVal
would be 80 as well as the value of i
. And this is indeed what happens according to godbolt.org:
foo: # @foo
push rbp
mov rbp, rsp
mov qword ptr [rbp - 8], rdi
mov dword ptr [rbp - 12], 8
imul eax, dword ptr [rbp - 12], 10
mov dword ptr [rbp - 12], eax
mov rdi, qword ptr [rbp - 8]
mov dword ptr [rdi], eax
pop rbp
ret
However after checking the operator precedence from here, the precedence of =
is higher than the precedence of *=
, so it seems that the value of *pVal
should be 8 and the value of i
should be 80...
What am I missing here?
EDIT:
In addition to the great answer by melpomene, there is also a nice Wikipedia page.
The table you linked to shows all assignment operators (including =
and *=
) at the same precedence level: 14.
They're also right associative, so x = y *= z
parses as x = (y *= z)
, as expected.
If =
had a higher precedence than *=
, then x = y *= z
would parse as (x = y) *= z
, which would be a hard error because =
doesn't yield an lvalue (you cannot assign to the result of an assignment). If =
did return its left operand as an lvalue, then (x = y) *= z
would have undefined behavior because it's modifying x
twice without an intervening sequence point. And if there were a sequence point after the inner assignment, the final values of the variables after (*pVal = i) *= 10
would be i = 8
(unmodified) and *pVal = 80
.
Live demo (using Perl, which has a =
that returns an lvalue as described above)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With