Cătălina Sîrbu
Cătălina Sîrbu

Reputation: 1283

Is bit field a way of aligning data?

From the cppreference

#include <iostream>
struct S {
    // will usually occupy 2 bytes:
    // 3 bits: value of b1
    // 2 bits: unused
    // 6 bits: value of b2
    // 2 bits: value of b3
    // 3 bits: unused
    unsigned char b1 : 3, : 2, b2 : 6, b3 : 2;
};
int main()
{
    std::cout << sizeof(S) << '\n'; // usually prints 2
}

Why is that 2 bits unused?

Couldn't be just:

unsigned char b1 : 3, b2 : 6, b3 : 2;

Is this a way of padding?

Could anyone explain me if (if I am not wrong) this bit fields actually change the size of the variable or they are just a "suggestion" for the compiler? (as inline keyword). If bit fields works as I understand, you would be able to stor a boolean on 1 bit (which is not possible because the memory is splitted on chunks of 1 bytes)

Upvotes: 1

Views: 395

Answers (2)

SergeyA
SergeyA

Reputation: 62583

The example on cppreference just tells that there is no guarantee in the standard that bitfields are mapped to adjacent memory regions, although most sensible implementations would do that. An code snippet provided deliberately uses an unnamed bitfiled of 2 bits to request two bits from the storage - and just showcases that bitfileds could be unnamed. This makes the total number of bits required to represent this structure 14.

14 bits could be packed into 2 bytes (16 bits), so this is what is expected of an average implementation - but it is not guaranteed to be happening.

Last, but not the least, if you find yourself in a position of using bitfields on a struct, think not twice, but triple. There are very few scenarios when they actually provide advantage, so you need to be sure you are dealing with one of those.

Upvotes: 3

Waqar
Waqar

Reputation: 9331

The example you quoted is being used to explain that multiple bit-fields are usually (not necessarily) packed together. If you look at it more carefully:

    // 3 bits: value of b1      - 3 bits occupied
    // 2 bits: unused           - 2 bits left unused
    // 6 bits: value of b2      - 6 bits occupied (these 6 can't fit into the previous byte
                                as 5 bits are already occupied. What will happen in this 
                                case? Will these 6 bits go into the next byte? Will they
                                somehow *overlap* two bytes?
    // 2 bits: value of b3       
    // 3 bits: unused

If the result of sizeof(S) == 2 is true, then we can say that the field b2 is straddling bytes. The example is trying to explain this concept.

The example is not so clear at explaining this, so let me create a slightly better one:

struct S {
    unsigned char b1 : 3, : 2, b2 : 6, b3 : 3;
};

The difference is b3 is now 3 bits. Total bits of the struct = 3 + 2 + 6 + 3 = 14. If I print the sizeof (S) now, I get, 3 as output which tells me there is no byte straddling on my system.

Furthermore, You can find this at the bottom of the page:

  • Everything about the actual allocation details of bit fields within the class object
    • For example, on some platforms, bit fields don't straddle bytes, on others they do
    • Also, on some platforms, bit fields are packed left-to-right, on others right-to-left

Could anyone explain me if (if I am not wrong) this bit fields actually change the size of the variable or they are just a "suggestion" for the compiler? (as inline keyword). If bit fields works as I understand, you would be able to stor a boolean on 1 bit (which is not possible because the memory is splitted on chunks of 1 bytes)

Almost everything about bit-fields is implementation defined so to get the correct answers you will have to look at each compiler's documentation and read the ABI documents to get the answer. For example, this is from GCC docs:

Whether a bit-field can straddle a storage-unit boundary (C90 6.5.2.1, C99 and C11 6.7.2.1).
Determined by ABI.

If we look at the assembly generated for GCC 10.1, we can see that the bit-fields are actually being used:

        # b1: 1, b2: 61, b3: 3

        sub     rsp, 24
        mov     eax, -767
        lea     rdi, [rsp+14]
        mov     WORD PTR [rsp+14], ax

The number -767 in binary:

b3  b6    b1
11 111101 00000001

boolean on 1 bit

The answer would not be complete without mentioning std::vector<bool> which tried to do this, but it turned out that it was not such a great idea.

Upvotes: 3

Related Questions