can I trust that the C compiler does modulo 2^n each time I access a bit field? Or is there any compiler/optimisation where a code like the one below would not print out Overflo
Yes. We can get the answer from assembly. Here is a example I code in Ubuntu 16.04, 64bit, gcc.
#include
typedef unsigned int uint32_t;
struct {
uint32_t foo1:8;
uint32_t foo2:24;
} G;
int main() {
G.foo1 = 0x12;
G.foo2 = 0xffffff; // G is 0xfffff12
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
G.foo2++; // G.foo2 overflow
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
G.foo1 += (0xff-0x12+1); // // G.foo1 overflow
printf("G.foo1=0x%02x, G.foo2=0x%06x, G=0x%08x\n", G.foo1, G.foo2, *(uint32_t *)&G);
return 0;
}
Compile it with gcc -S <.c file>
. You can get the assembly file .s
. Here I show the assembly of G.foo2++;
, and I write some comments.
movl G(%rip), %eax
shrl $8, %eax # 0xfffff12-->0x00ffffff
addl $1, %eax # 0x00ffffff+1=0x01000000
andl $16777215, %eax # 16777215=0xffffff, so eax still 0x01000000
sall $8, %eax # 0x01000000-->0x00000000
movl %eax, %edx # edx high-24bit is fool2
movl G(%rip), %eax # G.foo2, tmp123
movzbl %al, %eax # so eax=0x00000012
orl %edx, %eax # eax=0x00000012 | 0x00000000 = 0x00000012
movl %eax, G(%rip) # write to G
We can see that compiler will use shift instructions to ensure what you say.(note: here's memory layout of G is:
----------------------------------
| foo2-24bit | foo1-8bit |
----------------------------------
Of course, the result of aforementioned is:
G.foo1=0x12, G.foo2=0xffffff, G=0xffffff12
G.foo1=0x12, G.foo2=0x000000, G=0x00000012
G.foo1=0x00, G.foo2=0x000000, G=0x00000000