GCC has recently implemented alignment support. In this post I will discuss what this is and how you can use it.
There are four additions that are related to alignment support, these are the alignas alignment-specifier, the alignof expression, alignment arithmetic library support and
std::align for aligning pointers. I will explain these all individually below.
Alignment is the requirement that the memory address of the first byte of certain types of objects be modulo zero with respect to some integer. For example,
int32_t has an alignment of 4, it can only be placed in memory locations divisible by 4.
Alignments are ordered from weaker to stronger or stricter. An alignment that is stricter than another has a larger value. It is said to be stricter because there are less memory locations at which it can be placed, therefore the compiler is stricter in where that object can be placed.
An alignof expression is used to get the alignment of a type, or a reference to a complete object. For example, if you have a variable declared
int32_t x;, then
alignof(x) is 4.
If you ask for the alignment of an array type, the value is the alignment of the elements of the array.
The alignas keyword allows you to specify that a variable or data member of a class has a stricter alignment than that which it inherently has. An alignas declaration is placed before a variable. For example, to declare that the variable
x has the same alignment as
int64_t, you would write:
alignas(int64_t) int32_t x;
The value provided can be either a number or a type, when it is a type
type-id it is the same as writing
The alignment specified cannot be less strict than the alignment that would normally be required for that entity.
Library alignment arithmetic
The library provides a class template called
aligned_storage, its declaration is as follows:
template <std::size_t Len, std::size_t Alignment> struct aligned_storage;
It provides a member typedef
type which is a POD type suitable for use as storage for any object whose length is no greater than
Len and whose alignment is a divisor of
Alignment. For example, to declare some memory that can store anything aligned up to 8 and no longer than 20, you would write the following:
typedef std::aligned_storage<20, 8>::type MyAligned; MyAligned x;
This addition allows the variant class that I wrote previously to be implemented a little more cleanly. The member variable
m_storage can now be declared as follows:
typedef typename std::aligned_storage< max<Sizeof, First, Types...>::value, max<Alignof, First, Types...>::value >::type AlignedStorage; AlignedStorage m_storage;
Aligning pointers at runtime
The library also provides a function
std::align, that computes a pointer with the alignment requested. Its declaration is
void* align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t space) noexcept;
The idea is that you have some memory starting at
ptr, of at least length
space, and you want the first possible address inside that space that has length
size and aligned to
ptr is not at the required alignment, it will be moved along as long as there is space to do so.
Suppose that you have a ptr to memory address 0xF (I know that’s not valid memory but it’s easiest for the example), you have 20 bytes remaining in that space, you want an alignment of 4 and length 8, you would call:
size_t space = 20; auto result = align(4, 8, ptr, space);
and ptr would be incremented to
0x10, and space would be changed to 19, it would also return the value
If there is no valid memory for the request, then nullptr is returned. This would happen if moving to the right alignment doesn’t leave enough space.