Question

If I have a C++ struct, defining a 64bit data word such as..

struct SMyDataWord
{
    int Name : 40;
    int Colour : 24;
};

What does the : 40 syntax mean... does it mean that the first 40 bits are reserved for the Name and the remaining 24 bits for the Colour?

This is how it appears to be being used, but I've not come across it before.

Was it helpful?

Solution

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.

OTHER TIPS

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).

That's a bitfield definition.

Name is an integer that's able to store exactly 40 bits of information. Colour can store 24 bits.

This is often done to save some space in often needed structures, or compress code down to a size that's easy to handle for the CPU (in your case 64 bits. Fit's exactly into a CPU register on a 64 bit machine).

The code that accesses the bitfields will execute a tad slower though.

Use them judiciously:

Remember that almost everything about bit fields is implementation dependent. For example, whether bits are stored left-to-right or right-to-left depends on the actual hardware architecture. Furthermore, each compiler uses a different member alignment model, which is why the size of the optimized BillingRec is 12 bytes rather than 9. You cannot take a bit field's address nor can you create an arrays of bits. Finally, on most implementations the use of bit fields incurs speed overhead. Therefore, when you optimize your code, measure the effect of a certain optimization and its tradeoffs before you decide to use it.

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 ints, 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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top