What does C++ struct syntax "a : b" mean
Bitfields, carried over from C. Name
is 40 bits wide, Colour
is 24 bits wide. Your struct therefore has at least 64 bits. On my system 64 bits would be 8 bytes.
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
Yes, that is the syntax for bitfields. They are commonly used to define structs that map onto hardware registers. There are some things to keep in mind if you decide to use them, one is that you can't know how the compiler does the layout, ordering and padding in the actual bytes making up the fields can and will differ among compilers (and perhaps with the same compiler but with different optimization settings, too).