Search code examples
cgccstructunionsbit-fields

Why aren't bitfields allowed with normal variables?


I wonder why bitfields work with unions/structs but not with a normal variable like int or short.
This works:

struct foo {
    int bar : 10;
};

But this fails:

int bar : 10; // "Expected ';' at end of declaration"

Why is this feature only available in unions/structs and not with variables? Isn't it technical the same?


Edit:

If it would be allowed you could make a variable with 3 bytes for instance without using the struct/union member each time. This is how I would to it with a struct:

struct int24_t {
    int x : 24 __attribute__((packed));
};

struct int24_t var; // sizeof(var) is now 3
// access the value would be easier:
var.x = 123;

Solution

  • This is a subjective question, "Why does the spec say this?" But I'll give it my shot.

    Variables in a function normally have "automatic" storage, as opposed to one of the other durations (static duration, thread duration, and allocated duration).

    In a struct, you are explicitly defining the memory layout of some object. But in a function, the compiler automatically allocates storage in some unspecified manner to your variables. Here's a question: how many bytes does x take up on the stack?

    // sizeof(unsigned) == 4
    unsigned x;
    

    It could take up 4 bytes, or it could take up 8, or 12, or 0, or it could get placed in three different registers at the same time, or the stack and a register, or it could get four places on the stack.

    The point is that the compiler is doing the allocation for you. Since you are not doing the layout of the stack, you should not specify the bit widths.

    Extended discussion: Bitfields are actually a bit special. The spec states that adjacent bitfields get packed into the same storage unit. Bitfields are not actually objects.

    1. You cannot sizeof() a bit field.

    2. You cannot malloc() a bit field.

    3. You cannot &addressof a bit field.

    All of these things you can do with objects in C, but not with bitfields. Bitfields are a special thing made just for structures and nowhere else.

    About int24_t (updated): It works on some architectures, but not others. It is not even slightly portable.

    typedef struct {
        int x : 24 __attribute__((packed));
    } int24_t;
    

    On Linux ELF/x64, OS X/x86, OS X/x64, sizeof(int24_t) == 3. But on OS X/PowerPC, sizeof(int24_t) == 4.

    Note the code GCC generates for loading int24_t is basically equivalent to this:

    int result = (((char *) ptr)[0] << 16) |
                 (((unsigned char *) ptr)[1] << 8) |
                 ((unsigned char *)ptr)[2];
    

    It's 9 instructions on x64, just to load a single value.