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