Why this union's size is 2 with bitfields?

前端 未结 6 1241
独厮守ぢ
独厮守ぢ 2021-01-06 15:14

I am working on turbo C on windows where char takes one byte.Now my problem is with the below union.

union a
{
 unsigned char c:2;
}b;
void main()
{
printf(\         


        
相关标签:
6条回答
  • 2021-01-06 16:00

    Turbo C is based on 8086 microprocessor which has two byte word boundary. The atomic reading and writing is typically bound to CPU's architecture, so the compiler is adding some slack bytes to align your data structure.

    alt text

    Calling #pragma pack(1) may be able to disable it, but not sure if it works on Turbo C.

    0 讨论(0)
  • 2021-01-06 16:00

    There is a lot of misinformation in the answers so I will clarify. It could be for one of 2 reasons (I am not familiar with the compiler).

    1. The bitfield storage unit is 2.

    2. Alignment is forced to word (2 byte) boundary.

    I doubt it is the first case as it is a common extension to take the bitfield storage unit as the size of the declared "base" type. In this case the type is char which always has a size of 1.

    [In standard you can only declare bitfields of type int or unsigned int and the "storage unit" in which bitfields are grouped is fixed (usually the same size as an int). Even a single bit bitfield will use one storage unit.]

    In the 2nd case it is common for C compilers to implement #pragma pack to allow control of alignment. I suspect the default packing is 2 in which case a pad byte will be added at the end of the union. The way to avoid this is to use:

    #pragma pack(1)
    

    You should also use #pragma pack() afterward to set back to the default (or even better use the push and pop arguments if supported by your compiler).

    To all the repliers who said that you must put up with what the compiler does, this is contrary to the spirit of C. You should be able to use bitfields to map to any size or bit order in situations where you have no control over it such as a file format or hardware mapping.

    Of course this is highly non-portable since different implementations have different byte orders, orders that bits are added to a bitfield storage unit (from top or bottom), storage units size, default alignment etc.

    As to your 2nd question, I can't see the problem, though I never use scanf as it is problematic.

    0 讨论(0)
  • 2021-01-06 16:02

    There is a paragraph in the standard that states there shall be no padding before the first member of a struct. But it does not say explicitly so about unions. The difference in size could come because it wants to align the union at 2 byte boundaries, but as it cannot pad before the first member of a struct, the struct will have one byte aligning. Also note that an union could have more members with different types, which could widen the required alignment of your union. There could be reasons for the compiler to give them at least 2 bytes alignment, for example to ease code that has to handle according the required aligment of an union.

    Anyway, there is no requirement that your union should be one byte exactly. It just has to have place for all its members.

    Here is what the C standard has to say about your second question:

    The operand of the unary & operator shall be either a function designator or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

    So your best bet is to use your way using the int. you may put braces around the code, so the temporary variable is kept local:

    void func(void) { struct bits f; { int x; scanf("%d", &x); f.bitfield = x; } /* ... */ }
    
    0 讨论(0)
  • 2021-01-06 16:03

    In addition to the fact that there "there may also be unnamed padding at the end of a structure or union", the compiler is permitted to place a bitfield in "any addressable storage unit large enough to hold a bit-field". (both quotes are from the C90 standard - there is similar, but different, wording tin the C99 standard).

    Also note that the standard says that a "bit-field shall have a type that is a qualified or unqualified version of int, unsigned int, or signed int", so having a bit-field in a char type is non-standard.

    Because the behavior of bitfields are so dependent on unspecified compiler implementation details (there are several other non-portable issues with bit-fields that I have not mentioned) using them is almost always a bad idea. In particular, they are a bad idea when you are trying to model bit-fields in a file format, network protocol, or hardware register.


    More information from another SO answer:

    In general you should avoid bitfields and use other manifest constants (enums or whatever) with explicit bit masking and shifting to access the 'sub-fields' in a field.

    Here's one reason why bitfields should be avoided - they aren't very portable between compilers even for the same platform. from the C99 standard (there's similar wording in the C90 standard):

    An implementation may allocate any addressable storage unit large enough to hold a bitfield. 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.

    You cannot guarantee whether a bit field will 'span' an int boundary or not and you can't specify whether a bitfield starts at the low-end of the int or the high end of the int (this is independant of whether the processor is big-endian or little-endian).

    0 讨论(0)
  • 2021-01-06 16:05

    I'm not sure where you find the requirement that the union must be precisely the minimum size. An object must be at least as big as its members, but that is a lower bound only.

    You can't take the address of a bitfield; what would be its type? It can't be int*. scanf(%d) will write sizeof(int) * CHAR_BIT bits to the int* you pass in. That's writing more than 2 bits, yet you don't have that space.

    0 讨论(0)
  • 2021-01-06 16:08

    Compilers are allowed to add padding to structs and unions and while, I admit, that it's a little surprising that yours does round up the union to a two byte size when you are able to get a one byte struct it is perfectly allowed.

    In answer to your second question: no it's not avoidable. Bit fields are a struct packing optimization and the performance and convenience penalty to pay is that bit field members are not individually addressable.

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