Search code examples
c++gccunionsbit-fieldsmemory-layout

Alignment of bitfields inside unions


I'm a bit puzzled by how the following code gets layed out in memory:

struct Thing
{
    union
    {
        unsigned value:24;
        uint8_t bytes[3];
    };
    Thing(int v)
            :value(v)
    {}

    void foo()
    {
        printf("Thing %p value=%d !\n", this, value);
    }

} __attribute__((__packed__));

On gcc 3.3, 4.3 or 4.6 on Linux (without any special options I can think of - only "-Wall -g" on 4.6), the size of the structure is always 4:

$ pahole ./union
struct Thing {
        union {
                unsigned int               value;                /*           4 */
                unsigned char              bytes[3];             /*           3 */
        };
[...]

We had some similar code here where we had the unsigned value:24 in a structure, and somebody added the union and inadvertently increased the size of the struct from 3 to 4 bytes. The same thing happens if I try to define the union as "packed" - size is still 4. Is this behavior as per the C++ spec? What would be the explanation?

later edit: replaced "C spec" with "C++ spec".


Solution

  • You missed the packed attributes to your anonymous union. Consider this example:

    #define PACKED __attribute__((__packed__))
    struct S1 { unsigned value:24; } PACKED ;
    struct S2 { union { char a[3]; unsigned value:24; };  } PACKED ;
    struct S3 { union { char a[3]; unsigned value:24; } PACKED ;  };
    
    
    int main() {
       std::cout << sizeof(S1) << std::endl;
       std::cout << sizeof(S2) << std::endl;
       std::cout << sizeof(S3) << std::endl;
    }
    

    Output:

    3
    4
    3
    

    The packed attribute is a little weird, I always try and test every possible combination to get proper result.