C Alignment specifier: _Alignas

In this blog post, you will learn the alignment specifier _Alignas in C and their concept. You will learn how to use _Alignas alignment specifiers in your code and their effect. We also see some programming examples to understand the alignment specifiers. So first let’s understand what is the meaning of alignment.

We know that CPUs read and write memory more efficiently when they store data at an address that’s a multiple of the data size. For example, a 4-byte integer is accessed more efficiently if it’s stored at an address that’s a multiple of 4.

When data is not aligned, the CPU does more address calculation work to access the data. But you don’t need to worry about alignment. The compiler generally aligns data on natural boundaries that are based on the target processor and the size of the data. You can read this article, “Understanding of structure padding“.

I am not going into detail but I believe now you have some understanding of the alignment. So let’s understand what is _Alignas.

 

What is _Alignas (alignment specifiers)?

The_Alignas alignment specifier was introduced in C11. It specifies custom alignment for a variable or user-defined type. They can be applied to a struct, union, enumeration, or variable. But you need to remember that alignment specifier can not be used in conjunction with either of the storage-class specifier’s typedef or register, nor in a declaration of a function or bit-field.

_Alignas Syntax:

_Alignas ( constant expression )	(1)	(since C11)


_Alignas ( type )	(2)	(since C11)

1. constant-expression - The constant expression must be an integer constant expression whose value is a valid alignment or zero. If the expression evaluates to zero, it does not affect other alignment specifications in the same declaration.

2. type - Any type name.

 

Note: The first form is equivalent to _Alignas(_Alignof( type-name)).

 

You should specify an alignment in the form of a power of two, such as 1, 2, 4, 8, 16, and so on. Also, you must not use a value smaller than the size of the type. C standard states that “An object shall not be declared with an over-aligned type with an extended alignment requirement not supported by the implementation for an object of that storage duration”.

Let’s see an example to understand the _Alignas alignment specifier.

#include <stdio.h>
#include <stdalign.h>

/*this struct will be 32-byte aligned
  because alignedMemory is 32-byte aligned
  and is the largest alignment specified in the struct*/
typedef struct
{
    int value; //There will be 28 bytes of padding between value and _Alignas
    _Alignas(32) char alignedMemory[32];
} data;

int main()
{
    printf("sizeof(data): %d\n", sizeof(data));
    printf("alignof(data): %d\n", alignof(data));

    return 0;
}

Output:

_Alignas

Explanation:

A struct and union types have an alignment equal to the largest alignment of any member. The compiler inserts some extra bytes between the members of the structure or union for the alignment. These extra unused bytes are called padding bytes.

 

The C standard states that when multiple alignment specifiers occur in a declaration, the effective alignment requirement is the strictest specified alignment. So if there are several _Alignas specifiers use with struct several members, the alignment of the struct will be at least the value of the largest specifier. Let’s see an example for a better understanding.

#include <stdio.h>
#include <stdalign.h>


typedef struct
{
    int value;
    _Alignas(32) char alignedMemory1[32];
    _Alignas(64) char alignedMemory2[32];
} data;

int main()
{
    printf("sizeof(data): %d\n", sizeof(data));
    printf("alignof(data): %d\n", alignof(data));

    return 0;
}

Output:

sizeof(data): 128
alignof(data): 64

 

Note: The header <stdalign.h> defines macro alignof and alignas, which are map directly to _Alignof and _Alignas, respectively.

 

In the last, I want to explain very important points, If declarations of an object in different translation units have different alignment specifiers, the behavior is undefined. For example, if you have used _Alignas(4) during the definition of an object and now in another place, you are using _Alignas(8) for the declaration of the same object. The behavior is undefined (UB).

 

Recommended Post