Here sizeof
nicely demonstrates what's going on under the hood:
#include <iostream>
#include <climits>
struct bc_1 {
int a : 1;
int b : 1;
};
struct bc_2 {
int a : 31;
int b : 1;
};
struct bc_3 {
int a : 32;
int b : 1;
};
struct bc_4 {
int a : 31;
int b : 2;
};
struct bc_5 {
int a : 32;
int b : 32;
};
struct bc_6 {
int a : 40;
int b : 32;
};
struct bc_7 {
int a : 63;
int b : 1;
};
int main(int argc, char * argv[]) {
std::cout << "CHAR_BIT = " << CHAR_BIT;
std::cout << " => sizeof(int) = " << sizeof(int) << std::endl;
std::cout << "1, 1: " << sizeof(struct bc_1) << std::endl;
std::cout << "31, 1: " << sizeof(struct bc_2) << std::endl;
std::cout << "32, 1: " << sizeof(struct bc_3) << std::endl;
std::cout << "31, 2: " << sizeof(struct bc_4) << std::endl;
std::cout << "32, 32: " << sizeof(struct bc_5) << std::endl;
std::cout << "40, 32: " << sizeof(struct bc_6) << std::endl;
std::cout << "63, 1: " << sizeof(struct bc_7) << std::endl;
}
What follows depends on your compiler and OS, and possibly on your hardware. On macOS with gcc-7 (with a CHAR_BIT
= 8, a 32 bit int
(i.e. half of 64 bit long
) has sizeof(int)
= 4) this is the output I see:
CHAR_BIT = 8 => sizeof(int) = 4
1, 1: 4
31, 1: 4
32, 1: 8
31, 2: 8
32, 32: 8
40, 32: 12
63, 1: 8
This tells us several things: if both fields of int
type fit into a single int
(i.e. 32 bits in the example above), the compiler allocates only a single int
's worth of memory (bc_1
and bc_2
). Once, a single int
can't hold the bitfields anymore, we add a second one (bc_3
and bc_4
). Note that bc_5
is at capacity.
Interestingly, we can "select" more bits than allowed. See bc_6
. Here g++-7 gives a warning:
bitfields.cpp::30:13: warning: width of 'bc_6::a' exceeds its type
int a : 40;
^~
Note that: clang++ explains this in better detail
bitfields.cpp:30:9: warning: width of bit-field 'a' (40 bits) exceeds the width of its type; value will be truncated to 32 bits [-Wbitfield-width]
int a : 40;
^
However it seems that under the hood, the compiler allocates another int
's worth of memory. Or at the very least, it determines the correct size. I guess the compiler is warning us not to access this memory as int a = bc_6::a
(I would wager that int a
would then only have the first 32 bits of field bc_6::a
...). This is confirmed by bc_7
whose total size is that of two int
s, but the first field covers most of them.
Finally replacing int
with long
in the example above behaves as expected:
CHAR_BIT = 8 => sizeof(long) = 8
1, 1: 8
31, 1: 8
32, 1: 8
31, 2: 8
32, 32: 8
40, 32: 16
63, 1: 8