When GCC compiles this program, the assembly language output includes the sequence
movzbl (%rax), %eax
movzbl %al, %eax
movl %eax, -4(%rbp)
which does the following:
- Copy 32 bits from
*foo
(denoted by (%rax)
in assembly) to the register %eax
and fill in the higher-order bits of %eax
with zeros (not that there are any, because %eax
is a 32-bit register).
- Copy the low-order 8 bits of
%eax
(denoted by %al
) to %eax
and fill in the higher-order bits of %eax
with zeros. As a C programmer you would understand this as %eax &= 0xff
.
- Copy the value of
%eax
to 4 bytes above %rbp
, which is the location of bar
on the stack.
So this code is an assembly-language translation of
int bar = *foo & 0xff;
Clearly GCC has optimized the line based on the fact that a bool
should never hold any value other than 0 or 1.
If you change the relevant line in the C source to this
int bar = *((int*)foo) ? 1 : 0;
then the assembly changes to
movl (%rax), %eax
testl %eax, %eax
setne %al
movzbl %al, %eax
movl %eax, -4(%rbp)
which does the following:
- Copy 32 bits from
*foo
(denoted by (%rax)
in assembly) to the register %eax
.
- Test 32 bits of
%eax
against itself, which means ANDing it with itself and setting some flags in the processor based on the result. (The ANDing is unnecessary here, but there's no instruction to simply check a register and set flags.)
- Set the low-order 8 bits of
%eax
(denoted by %al
) to 1 if the result of the ANDing was 0, or to 0 otherwise.
- Copy the low-order 8 bits of
%eax
(denoted by %al
) to %eax
and fill in the higher-order bits of %eax
with zeros, as in the first snippet.
- Copy the value of
%eax
to 4 bytes above %rbp
, which is the location of bar
on the stack; also as in the first snippet.
This is actually a faithful translation of the C code. And indeed, if you add the cast to (int*)
and compile and run the program, you'll see that it does output 1
.