Why aren't bitfields allowed with normal variables?

前端 未结 5 1543
隐瞒了意图╮
隐瞒了意图╮ 2020-12-10 08:14

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

struct foo {
             


        
相关标签:
5条回答
  • 2020-12-10 08:51

    All objects must occupy one or more contiguous bytes or words, but a bitfield is not an object; it's simply a user-friendly way of masking out bits in a word. The struct containing the bitfield must occupy a whole number of bytes or words; the compiler just adds the necessary padding in case the bitfield sizes don't add up to a full word.

    There's no technical reason why you couldn't extend C syntax to define bitfields outside of a struct (AFAIK), but they'd be of questionable utility for the amount of work involved.

    0 讨论(0)
  • 2020-12-10 08:52

    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.

    0 讨论(0)
  • 2020-12-10 08:59

    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.

    0 讨论(0)
  • Because it's not meaningful. Bitfield declarations are used to share and reorganize bits between fields of a struct. If you have no members, just a single variable, that is of constant size (which is implementation-defined), For example, it's a contradiction to declare a char, which is almost certainly 8 bits wide, as a one or twelwe bit variable.

    0 讨论(0)
  • 2020-12-10 09:13

    If one has a struct QBLOB which contains combines four 2-bit bitfields into a single byte, every time that struct is used will represent a savings of three bytes as compared with a struct that simply contained four fields of type unsigned char. If one declares an array QBLOB myArray[1000000], such an array will take only 1,000,000 bytes; if QBLOB had been a struct with four unsigned char fields, it would have needed 3,000,000 bytes more. Thus, the ability to use bitfields may represent a big memory savings.

    By contrast, on most architectures, declaring a simple variable to be of an optimally-sized bitfield type could save at most 15 bits as compared with declaring it to be the smallest suitable standard integral type. Since accessing bitfields generally requires more code than accessing variables of standard integral types, there are few cases where declaring individual variables as bit fields would offer any advantage.

    There is one notable exception to this principle, though: some architectures include features which can set, clear, and test individual bits even more efficiently than they can read and write bytes. Compilers for some such architectures include a bit type, and will pack eight variables of that type into each byte of of storage. Such variables are often restricted to static or global scope, since the specialized instructions that handle them may be restricted to using certain areas of memory (the linker can ensure any such variables get placed where they have to go).

    0 讨论(0)
提交回复
热议问题