Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
like image 691
qwertz Avatar asked Dec 21 '22 12:12

qwertz


2 Answers

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.

like image 98
Dietrich Epp Avatar answered Dec 24 '22 00:12

Dietrich Epp


Members of a structure or union have relationships between their storage location. A compiler cannot reorder or pack them in clever ways to save space due to strict constraints on the layout; basically the only freedom a compiler has in laying out structures is the freedom to add extra padding beyond the amount that's needed for alignment. Bitfields allow you to manually give the compiler more freedom to pack information tightly by promising that (1) you don't need the address of these members, and (2) you don't need to store values outside a certain limited range.

If you're talking about individual variables rather than structure members, in the abstract machine they have no relationship between their storage locations. If they're local automatic variables in a function and their addresses are never taken, the compiler is free to keep them in registers or pack them in memory however it likes. There would be little or no benefit to providing such hints to the compiler manually.

like image 45
R.. GitHub STOP HELPING ICE Avatar answered Dec 24 '22 00:12

R.. GitHub STOP HELPING ICE