Home > OS >  What does __attribute__((packed)) do to a union?
What does __attribute__((packed)) do to a union?

Time:03-11

The spec (GCC type attributes amongst others) states that __attribute__((packed)) can be applied to a struct or a union. Since all union fields' storage overlap, what does the attribute actually do to a union? If you have various structures in the union and would like the structures to be packed, you have to specify the attribute to each individually.

CodePudding user response:

In addition to eliminating padding in an aggregate, packing reduces the alignment requirement to one byte, as we see in this code:

#include <stdio.h>


int main(void)
{
    {
        union normal { int i; };
        union packed { int i; } __attribute__((__packed__));
        printf("Alignment of normal union is %zu.\n", _Alignof (union normal));
        printf("Alignment of packed union is %zu.\n", _Alignof (union packed));
    }
}

which outputs:

Alignment of normal union is 4.
Alignment of packed union is 1.

A corollary of this, as noted in Adrian Mole’s answer, is that a packed union does not require padding at its end to make its size a multiple of its alignment.

As I explained in this answer, although older documentation said the packed attribute was recursively applied to members, it was poorly phrased. The packed attribute merely reduces the alignment requirement of each member to one byte, so that it may be located at any address. It does not reach into members that are also aggregates and eliminate padding from them. Hence this code:

#include <stdio.h>


int main(void)
{
    {
        union normal { struct { char c; int i; }; };
        union packed { struct { char c; int i; }; } __attribute__((__packed__));
        printf("Size of normal union is %zu.\n", sizeof (union normal));
        printf("Size of packed union is %zu.\n", sizeof (union packed));
    }
}

outputs:

Size of normal union is 8.
Size of packed union is 8.

CodePudding user response:

One effect (there are others) of applying the the packed attribute to a union is to remove any padding added at the end of the union. Here is a contrived example:

#include <stdio.h>

typedef union carr {
    char a[3];
    short b;
} Carr;

typedef union __attribute__((packed)) parr {
    char a[3];
    short b;
} Parr;

int main()
{
    printf("Unpacked: %zu, Packed: %zu\n", sizeof(Carr), sizeof(Parr));
    return 0;
}

Output (clang-cl, Visual Studio 2019, Windows 10):

Unpacked: 4, Packed: 3

Note that the GCC 11.2 gives the same output: Compiler Explorer. Without packing, a padding byte is added to optimize the alignment of (and access to) the short b member, in cases like arrays of such unions.


Note that, despite the documentation1, the packed attribute is not propagated to structures nested in that union: Compiler Explorer; in this latter case, explicitly adding __attribute__((packed)) to the internal struct will reduce the overall size to 3 bytes.


1 This document:

Specifying this attribute for struct and union types is equivalent to specifying the packed attribute on each of the structure or union members.

  • Related