Working with Packing Structures

9/7/2007

Problems can occur when a structure requires more bytes than the programmer intended, especially when space requirements are paramount.

Structure Packing and Alignment

Structure packing interacts with compiler alignment behavior as follows.

  • If the packsize is set equal to or greater than the default alignment, the packsize is ignored.
  • If the packsize is set smaller than the default alignment, the compiler aligns according to the packsize value.

Thus, if the packsize is set to four, data types having a size of four, eight, or 16 bytes are aligned on addresses that are multiples of four. However, there is no guarantee that data types eight bytes in size (64 bits) are aligned on addresses that are a multiple of eight. The packsize has no effect on data types outside of a structure.

In addition, packing affects the alignment of the entire packed structure. For example, in a structure declared under #pragma pack(1), the alignment of all members are forced to one, regardless of whether they would have been naturally aligned even without packing.

The following techniques set a packsize, in bytes:

  • The command-line option /Zp (Struct Member Alignment) sets the packsize to n, in which n can be 1, 2, 4, 8, or 16, and in which 8 is the default.
  • The compiler directive #pragma pack([n]) sets the packsize to n, in which n can be 1, 2, 4, 8, or 16. If n is not specified, #pragma pack resets the packsize to its value at the beginning of compilation: either the value specified by /Zp (Struct Member Alignment), or the default, which is 8 on most platforms.
    The pragma applies only from the point at which it occurs in the source. For example, the /Zp1 option sets the packsize to 1, which causes the compiler to use no padding within structures. To avoid this problem, turn off structure packing or use the __unaligned keyword when accessing unaligned members of such structures through pointers.

Guidelines for packing structures

The following list shows possible solutions to the structure issue.

  • Reordering structure members
    If space requirements are critical, reorder the members of the structure so the same-sized elements are next to each other and pack tightly. Usually, you start with the largest members and work your way down to the smallest ones.
    Note that reordering a structure assumes that the user has full control of the data structure, but the user might not have the freedom to rearrange the members. For example, the data structure might represent the layout of fields in a file on disk.
    Consider the following code example:

    struct x_
    {
       char a;     // 1 byte
       int b;      // 4 bytes
       short c;    // 2 bytes
       char d;     // 1 byte
    } MyStruct;
    

    If you reorganize the members of this structure, as shown in the following code example, the reorganized structure aligns all members on natural boundaries, and the size of the structure is eight bytes instead of 12.

    struct x_
    {
       int b;     // 4 bytes
       short c;   // 2 bytes
       char d;    // 1 byte
       char a;    // 1 byte
    } MyStruct;
    
  • Padding the structure
    A different kind of problem can arise when the structure size requires padding to make sure array elements have the same alignment, but the user needs to ensure that there is no padding between the array elements. For example, the user might need to restrict memory usage or read data from a fixed-format source.
    If the structure requires padding, you can use the compiler directive #pragma pack. However, #pragma pack causes elements of the structures to be unaligned, and it requires the use of the __unaligned keyword qualifier to generate the code needed to access this data without causing alignment faults.
    The following example code uses #pragma pack to tell the compiler that the pointer px points to data that is not naturally aligned, and tells the compiler to generate the appropriate sequence of load, merge, and store operations to do the assignment efficiently.

    # pragma pack (1)
    struct x_
    {
       char a;    // 1 byte
       int b;     // 4 bytes
       short c;   // 2 bytes
    } MyStruct;
    # pragma pack ()
    
    void bar()
    {
      struct x_ __unaligned *px = &MyStruct;
       . . . .
       px->b = 5;
    }
    

    The __unaligned keyword should only be used as a last resort, because the generated code is less efficient than accessing naturally aligned data. However, the __unaligned keyword is clearly preferable to alignment faults.
    If at all possible, arrange the members of a data structure to preserve alignment and minimize space at the same time.

See Also

Reference

__unaligned keyword