Determining the Alignment of C/C++ Structures in Relation to Its Members

Determining the alignment of C/C++ structures in relation to its members

There are two closely related concepts to here:

  1. The alignment required by the processor to access a particular object
  2. The alignment that the compiler actually uses to place objects in memory

To ensure alignment requirements for structure members, the alignment of a structure must be at least as strict as the alignment of its strictest member. I don't think this is spelled out explicitly in the standard but it can be inferred from the the following facts (which are spelled out individually in the standard):

  • Structures are allowed to have padding between their members (and at the end)
  • Arrays are not allowed to have padding between their elements
  • You can create an array of any structure type

If the alignment of a structure was not at least as strict as each of its members you would not be able to create an array of structures since some structure members some elements would not be properly aligned.

Now the compiler must ensure a minimum alignment for the structure based on the alignment requirements of its members but it can also align objects in a stricter fashion than required, this is often done for performance reasons. For example, many modern processors will allow access to 32-bit integers in any alignment but accesses may be significantly slower if they are not aligned on a 4-byte boundary.

There is no portable way to determine the alignment enforced by the processor for any given type because this is not exposed by the language, although since the compiler obviously knows the alignment requirements of the target processor it could expose this information as an extension.

There is also no portable way (at least in C) to determine how a compiler will actually align an object although many compilers have options to provide some level of control over the alignment.

C++ Align by structure size or largest alignment requirement among its members?

The optimal alignment for a struct is equal to the largest alignment for any of the struct's members. In this case that is 4.

Update

The above assumes that the primary operation you perform on the struct is accessing its members. See the comments to Necrolis's answer for more discussion. In short I suspect that the real answer to your question depends strongly on the hardware involved and the algorithms you are using.

Using different structure alignment settings

The structure layout is determined by the alignment rules in affect at compilation time. For efficiency reasons, structure members are aligned in way as to make their addresses in memory multiples of 2, 4, 8 or sometimes larger values.

Extra padding in inserted between structure members depending on their sizes and alignment requirements, and extra padding may be added at the end of the structure to allow arrays of structures to keep this alignment valid for members of all array elements. malloc is guarantied to return a pointer aligned for all practical purposes:

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement.

This behavior can sometimes be controlled via compiler switches and or pragmas as seems to be possible with Visual C/C++.

The consequence is important for structures for which said options will produce different padding. The code to access structure members with be different in the various libraries and programs handling shared data, invoking undefined behaviour.

It is therefore strongly advised to not use different alignment options for data structures shared between different pieces of compiled code. Whether they live in different programs, dynamic libraries, statically linked object files, user or kernel code, device drivers, etc. the result is the same: undefined behavior with potential catastrophic consequences (like any UB).

It only matters for structures used and shared between the incompatible modules, but it is very difficult to keep track over time of who uses and who does not use structure definitions published in shared header files. Structures that are only used locally and whose definition is not visible should not pose a problem, but only for as long as they stay private.

I you use compile time switches, all structures will be affected by these, including standard definitions from the C library, and all structures defined locally or globally in your header files. Standard include files may have provisions to keep them immune from such discrepancies, but your own structure definitions will not.

Don't do this with compile time switches unless you know exactly why you need to do it. Don't do this in source code (via pragmas, attributes and other non portable constructs) unless you are a real expert with complete control over every use of your data.

If you are concerned with structure packing, reorder the struct members and chose their types appropriately to achieve your goals. Changing the compiler's packing rules in highly error prone. See http://www.catb.org/esr/structure-packing/ for a complete study.

Memory alignment in C-structs

At least on most machines, a type is only ever aligned to a boundary as large as the type itself [Edit: you can't really demand any "more" alignment than that, because you have to be able to create arrays, and you can't insert padding into an array]. On your implementation, short is apparently 2 bytes, and int 4 bytes.

That means your first struct is aligned to a 2-byte boundary. Since all the members are 2 bytes apiece, no padding is inserted between them.

The second contains a 4-byte item, which gets aligned to a 4-byte boundary. Since it's preceded by 6 bytes, 2 bytes of padding is inserted between v3 and i, giving 6 bytes of data in the shorts, two bytes of padding, and 4 more bytes of data in the int for a total of 12.

What's the reason for this struct's alignment?

VkSurfaceKHR is a non-dispatchable handle. Which, by Vulkan's definitions, is a 64-bit integer. And therefore it must have 8-byte alignment.

When structures are being laid out, the compiler will ensure that each member gets the alignment that the type requires. If surface had come immediately after flags, it would not have 8-byte alignment. Thus, the compiler inserts 4 bytes of padding between the two.

Structure padding and packing

Padding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default. It inserts the following "gaps" into your first structure:

struct mystruct_A {
char a;
char gap_0[3]; /* inserted by compiler: for alignment of b */
int b;
char c;
char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

Packing, on the other hand prevents compiler from doing padding - this has to be explicitly requested - under GCC it's __attribute__((__packed__)), so the following:

struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};

would produce structure of size 6 on a 32-bit architecture.

A note though - unaligned memory access is slower on architectures that allow it (like x86 and amd64), and is explicitly prohibited on strict alignment architectures like SPARC.



Related Topics



Leave a reply



Submit