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.