Share via


Alignment

Une des fonctionnalités de bas niveau de C++ est la possibilité de spécifier avec précision l’alignement des objets en mémoire afin d’exploiter au mieux les capacités d’une architecture matérielle spécifique. Par défaut, le compilateur aligne les membres de classe et de struct sur leur valeur de taille : bool et sur les limites de 1 octet, short sur les limites de 2 octets, et longfloatintsur les limites de 4 octets, et doublelong doublelong longsur les limites de 8 octets.char

Dans la plupart des scénarios, vous n’avez jamais à vous soucier de l’alignement, car l’alignement par défaut est déjà optimal. Dans certains cas, toutefois, vous pouvez obtenir des améliorations significatives des performances ou des économies de mémoire en spécifiant un alignement personnalisé pour vos structures de données. Avant Visual Studio 2015, vous pouvez utiliser les mot clé __alignof spécifiques à Microsoft et __declspec(align) spécifier un alignement supérieur à la valeur par défaut. À compter de Visual Studio 2015, vous devez utiliser les mot clé alignof standard C++11 et alignas pour une portabilité maximale du code. Les nouvelles mot clé se comportent de la même façon que les extensions spécifiques à Microsoft. La documentation de ces extensions s’applique également aux nouvelles mot clé. Pour plus d’informations, consultez alignof Opérateur, alignas Spécificateur et alignement. La norme C++ ne spécifie pas le comportement d’emballage pour l’alignement sur les limites inférieures à la valeur par défaut du compilateur pour la plateforme cible. Vous devez donc toujours utiliser Microsoft #pragma pack dans ce cas.

Utilisez la classe aligned_storage pour l’allocation de mémoire de structures de données avec des alignements personnalisés. La classe aligned_union est destinée à spécifier l’alignement pour les unions avec des constructeurs ou des destructeurs nontrivials.

Adresses d’alignement et de mémoire

L'alignement est une propriété d'une adresse mémoire, exprimée sous la forme de l'adresse numérique modulo une puissance de 2. Par exemple, l’adresse 0x0001103F modulo 4 est 3. Cette adresse est dite alignée sur 4n+3, où 4 indique la puissance choisie de 2. L’alignement d’une adresse dépend de la puissance choisie de 2. La même adresse modulo 8 est 7. Une adresse est considérée comme étant alignée sur X si son alignement est Xn+0.

Les processeurs exécutent des instructions qui fonctionnent sur les données stockées en mémoire. Les données sont identifiées par leurs adresses en mémoire. Une référence unique a également une taille. Nous appelons une référence naturellement alignée si son adresse est alignée sur sa taille. Elle est appelée mal alignée sinon. Par exemple, une référence à virgule flottante de 8 octets est naturellement alignée si l’adresse utilisée pour l’identifier a un alignement de 8 octets.

Gestion par le compilateur de l’alignement des données

Les compilateurs tentent d’effectuer des allocations de données d’une manière qui empêche l’alignement incorrect des données.

Pour les types de données simples, le compilateur assigne des adresses qui sont des multiples de la taille en octets du type de données. Par exemple, le compilateur affecte des adresses à des variables de type long multiples de 4, en définissant les 2 bits inférieurs de l’adresse sur zéro.

Le compilateur place également les structures d’une manière qui aligne naturellement chaque élément de la structure. Considérez la structure struct x_ dans l’exemple de code suivant :

struct x_
{
   char a;     // 1 byte
   int b;      // 4 bytes
   short c;    // 2 bytes
   char d;     // 1 byte
} bar[3];

Le compilateur remplit cette structure pour appliquer naturellement l'alignement.

L’exemple de code suivant montre comment le compilateur place la structure rembourrée en mémoire :

// Shows the actual memory layout
struct x_
{
   char a;            // 1 byte
   char _pad0[3];     // padding to put 'b' on 4-byte boundary
   int b;            // 4 bytes
   short c;          // 2 bytes
   char d;           // 1 byte
   char _pad1[1];    // padding to make sizeof(x_) multiple of 4
} bar[3];

Les deux déclarations sont retournées sizeof(struct x_) sous la forme de 12 octets.

La deuxième déclaration inclut deux éléments de remplissage :

  1. char _pad0[3] pour aligner le int b membre sur une limite de 4 octets.
  2. char _pad1[1] pour aligner les éléments de tableau de la structure struct _x bar[3]; sur une limite de 4 octets.

Le remplissage aligne les éléments d’une bar[3] manière qui permet un accès naturel.

L’exemple de code suivant montre la bar[3] disposition du tableau :

adr offset   element
------   -------
0x0000   char a;         // bar[0]
0x0001   char pad0[3];
0x0004   int b;
0x0008   short c;
0x000a   char d;
0x000b   char _pad1[1];

0x000c   char a;         // bar[1]
0x000d   char _pad0[3];
0x0010   int b;
0x0014   short c;
0x0016   char d;
0x0017   char _pad1[1];

0x0018   char a;         // bar[2]
0x0019   char _pad0[3];
0x001c   int b;
0x0020   short c;
0x0022   char d;
0x0023   char _pad1[1];

alignof et alignas

Le alignas spécificateur est un moyen standard portable C++ de spécifier l’alignement personnalisé des variables et des types définis par l’utilisateur. L’opérateur alignof est également un moyen standard et portable d’obtenir l’alignement d’un type ou d’une variable spécifié.

Exemple

Vous pouvez utiliser alignas sur une classe, un struct ou une union, ou sur des membres individuels. Lorsque plusieurs alignas spécificateurs sont rencontrés, le compilateur choisit celui avec la plus grande valeur.

// alignas_alignof.cpp
// compile with: cl /EHsc alignas_alignof.cpp
#include <iostream>

struct alignas(16) Bar
{
    int i;       // 4 bytes
    int n;      // 4 bytes
    alignas(4) char arr[3];
    short s;          // 2 bytes
};

int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}

Voir aussi

Alignement de la structure des données