2

Why is the sizeof a struct with bit fields not what I expected.

#include <iostream>
using namespace std;

struct test {
    uint8_t x : 4;
    uint16_t y : 8;
    uint16_t z : 8;
};

struct test2 {
    uint8_t x : 4;
    uint16_t y : 10;
    uint16_t z : 10;
};

int main()
{
  cout << sizeof(test) << endl;
  cout << sizeof(test2) << endl;
}

This prints 4 and 4. I don't understand why both of these do not have a size of 3. Test has 4+8+8 bits which is 20 and Test2 has 4+10+10 bits which is 24 bits, both less than or equal to 24 bits/3 bytes. I know that if in Test I use uint_8 it would result in a size of 3 but for my actual use case I need Test2(4,10,10 bits). Why is this and is there a way to get this to 3 bytes?

jujumumu
  • 350
  • 4
  • 12
  • Consider this https://stackoverflow.com/q/1841863/10901306 – Zukaru Oct 12 '21 at 03:40
  • Familiarize yourself with structure padding, and the available options for your compiler to adjust the padding, such as `#pragma pack` or similar construct. – PaulMcKenzie Oct 12 '21 at 03:43
  • Look up "struct padding". The details are a little different for bitfields, but the principles are the same. Similarly, most compilers support options (e.g. command line options or `#pragma`s) that can control padding. Disabling (or explicitly controlling) padding has a number of impacts (e.g. performance, CPU may trigger hardware exceptions that need to be dealt with for the code to behave correctly) - it's not always true (particularly with modern compilers and platforms) that saving space through struct padding is beneficial. – Peter Oct 12 '21 at 03:48
  • If you compile with `/Wall`, some compilers show a warning when padding is added after a data member – Eyal K. Oct 12 '21 at 06:12

4 Answers4

1

Why is this

std::uint16_t presumably has alignment requirement of 2 on your system. As such, the first valid offset where the member could be placed is the third byte (offset of 2 which is divisible by the alignment). Since there is nothing that can be stored at the second byte (offset 1), it would be useless padding.

is there a way to get this to 3 bytes?

No, there's no standard way to get test2 to 3 bytes. For test it is easy by using std::uint8_t for all members.

There's also probably no necessity to get it to 3 bytes. If you do need exact bit layout, then the alignment isn't your only concern since the layout of bitfields isn't standard in any way.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

No only bitfields, but all the structures are aligned by the compiler to get the maximum efficiency. If you want to force them to the minimum size you need to use the gcc's attribute packed or the equivalent in compiler you are using, like following:

#include <iostream>
using namespace std;

struct test {
    uint8_t x : 4;
    uint16_t y : 8;
    uint16_t z : 8;
} __attribute__ ((packed));

struct test2 {
    uint8_t x : 4;
    uint16_t y : 10;
    uint16_t z : 10;
} __attribute__ ((packed));

int main()
{
  cout << sizeof(test) << endl;
  cout << sizeof(test2) << endl;
}
Carlo Banfi
  • 186
  • 1
  • 7
1

Data structure alignment would help you explain the output: https://en.wikipedia.org/wiki/Data_structure_alignment

The data structure is adding more space and it is called padding. You also need to check your compiler, cause different compilers have different default options for structure alignment and will have different output, for example, in VS you can set it at here: enter image description here

Beside, you might want to check #pragma pack: https://learn.microsoft.com/en-us/cpp/preprocessor/pack?redirectedfrom=MSDN&view=msvc-160

Le Ngoc Thuong
  • 279
  • 2
  • 8
0
#pragma pack( push, 1 )

struct test {
    uint8_t x : 4;
    uint16_t y : 8;
    uint16_t z : 8;
};

struct test2 {
    uint8_t x : 4;
    uint16_t y : 10;
    uint16_t z : 10;
};

#pragma pack( pop )

this will pack the structures by the alignment of 1 byte.