2

I found a code segment for a structure as below:

    struct
    {
      __u8 saveable: 1;
      __u8 namespace: 1;
      __u8 changeable: 1;
      __u32 reserved: 29;
    };

I wonder what the difference is between that and the definition as below:

    struct
    {
      __u32 saveable: 1;
      __u32 namespace: 1;
      __u32 changeable: 1;
      __u32 reserved: 29;
    };

Are there any possible reasons behind this?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
codexplorer
  • 541
  • 5
  • 21
  • 1
    I think the second struct will use just 1 32-bit memory and use all its bits. First struct will use 1 8-bit memory and 1 32-bit memory and 8 bits will be wasted (5 bits in _u8 and 3 bits in _u32). – kiner_shah Jul 29 '22 at 06:56
  • 2
    @kiner_shah Maybe. Or [maybe not](https://godbolt.org/z/Tj8EzfbGq). – Some programmer dude Jul 29 '22 at 06:58
  • @Someprogrammerdude, interesting. I am curious now, how this works? – kiner_shah Jul 29 '22 at 06:59
  • The answer I only have is about the size. Anything else? – codexplorer Jul 29 '22 at 07:03
  • @kiner_shah I agree with you. While many things are compiler implementation dependent when dealing with bitfields, the GCC behavior appears to be correct, at least strictly interpreting the standard. The padding depends on the packing attributes. Anyway since beginning of C language, bitfields have been the most uncompliant feature, Always use masking if your code must be compiled on different platforms. – Frankie_C Jul 29 '22 at 10:07
  • 1
    @kiner_shah In GCC using the switch -mms-bitfields it behaves as MS compiler. See https://godbolt.org/z/T5jfdfxfa. See also the SO answer https://stackoverflow.com/questions/11283164/a-bug-in-gcc-implementation-of-bit-fields. See my comment under below answer. – Frankie_C Jul 29 '22 at 10:11

1 Answers1

3

Although the allocation of objects that contain bit-fields is implementation-defined, the C Standard does specify that consecutive bit-field members of a structure shall be packed into the same 'unit', if sufficient space remains in that unit.

From this Draft C11 Standard (bold emphasis mine):

6.7.2.1 Structure and union specifiers


11    An implementation may allocate any addressable storage unit large enough to hold a bit-field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.

Thus, in your first structure, the first three fields could be packed into one 8-bit unit but, then, the fourth must be placed in a separate, 32-bit unit (which will likely require 4-byte alignment, thus increasing the overall size of the structure to 8 bytes). What also may be significant in such a case is that padding bytes will be added to the structure, which may cause issues if your code relies on contiguous bits.

However, in the second case, all four bit-fields can be placed in a single 32-bit unit, so the structure size can be reduced to just 4 bytes.

But note that this may vary between compilers and platforms – notably, as demonstrated in the Compiler Explorer example linked in the comments, an implementation is entitled to allocate a 32-bit unit for the first three members of the first structure.

On Windows, using the clang-cl compiler in Visual Studio 2022, I get the aforementioned size difference between the structures when compiling and running the following code:

#include <stdint.h>
#include <stdio.h>

struct {
    uint8_t saveable : 1;
    uint8_t namespace : 1;
    uint8_t changeable : 1;
    uint32_t reserved : 29;
} s1;

struct {
    uint32_t saveable : 1;
    uint32_t namespace : 1;
    uint32_t changeable : 1;
    uint32_t reserved : 29;
} s2;

int main()
{
    printf("Size of s1 = %zu\n", sizeof(s1));
    printf("Size of s2 = %zu\n", sizeof(s2));
    return 0;
}

Output:

Size of s1 = 8
Size of s2 = 4
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • See my comments above – Frankie_C Jul 29 '22 at 08:54
  • @Frankie_C I'm not 100% convinced that GCC is non-compliant in the example in the Godbolt link. The excerpt from the Standard I quoted (which is essentially unchanged in C17) suggests that the compiler can legally allocate a `uint32_t` for a bit-filed with a specified type of `uint8_t`. Can you provide a quote *from the Standard* that states that the fourth member (in OP's first snippet) **must** be in a separate storage unit? – Adrian Mole Jul 29 '22 at 09:07
  • 1
    Seems you're right, it isn't so evident anyway: While the standard specifically allows only `_Bool`, `signed int`, and `unsigned int` as bit-field types, then adds at the end `or some other implementation-defined type` (6.7.2.1 Structure and union specifiers). While for the first three, with some hesitation, comes obvious that the single bitfields can be packed in the same storage, this doesn't sound so 'natural' when the types are different. I'll fix my comments above. – Frankie_C Jul 29 '22 at 10:03