Alignment

C++ の低レベルの機能の 1 つは、特定のハードウェア アーキテクチャを最大活用するために、メモリ内のオブジェクトの正確な配置を指定できる機能です。 既定では、コンパイラはクラスと構造体のメンバーをそのサイズ値上に配置します。boolchar を 1 バイト境界、short を 2 バイト境界、intlongfloat を 4 バイト境界、long longdoublelong double を 8 バイト境界上に配置します。

ほとんどのシナリオで、既定の配置は既に最適なので、配置を気にする必要はありません。 しかし、場合によっては、大幅なパフォーマンスの向上、またはメモリの節約を、データ構造にカスタム配置を指定することで達成できます。 Visual Studio 2015 の前は、既定値を超える配列を指定するのに、Microsoft 固有キーワード __alignof__declspec(align) を使用できました。 Visual Studio 2015 以降、C++11 の標準キーワードの alignofalignas を使用して、コードの移植性を最大にする必要があります。 この新しいキーワードは、Microsoft 固有の拡張機能と同じように内部で動作します。 これらの拡張機能に関するドキュメントは、新しいキーワードにも適用されます。 詳細については、「演算子、指定子alignas配置」を参照してくださいalignof C++ 標準は、ターゲット プラットフォームに対するコンパイラの既定値よりも小さい境界の配置では、パッキング ビヘイビアーを指定しないため、その場合は Microsoft #pragma pack を引き続き使用する必要があります。

カスタム配置を使用するデータ構造のメモリ割り当てには、aligned_storage クラスを使用します。 aligned_union クラスは、非トリビアル コンストラクターまたはデストラクターを持つ共用体のアラインメントを指定するためのクラスです。

配置とメモリ アドレス

配置は、2 の累乗の数値のアドレスの剰余として表現される、メモリ アドレスのプロパティです。 たとえば、0x0001103F のアドレスを 4 で割ると 3 になります。 このアドレスは 4n+3 に配置されたということができます。4 は選択した 2 の累乗です。 アドレスの配置は、選択した 2 の累乗値に依存します。 同じアドレスの 8 の剰余は 7 です。 配置が Xn+0 の場合、アドレスが X に配置されると言われます。

CPU は、メモリに格納されているデータを操作する命令を実行します。 データは、メモリ内のアドレスによって識別されます。 単一のデータにもサイズがあります。 アドレスがサイズに合わせて配置されている場合は、データが自然に配置されていると呼びます。 そうでない場合は適切に配置されていないと呼びます。 たとえば、識別のために使用されるアドレスが 8 バイトの配置の場合、8 バイト浮動小数点のデータが自然に配置されます。

コンパイラによるデータ アラインメントの処理

コンパイラは、データが適切に配置されないのを防ぐ方法でデータ割り当てを試行します。

単純なデータ型の場合、コンパイラは、データ型のバイト単位のサイズの倍数であるアドレスを割り当てます。 たとえば、コンパイラは、4 の倍数である long 型の変数にアドレスを割り当てて、アドレスの下の 2 つのビットをゼロに設定します。

また、コンパイラは構造体の各要素が自然な配置になるように、構造体を埋めます。 次のコード例の構造体 struct x_ について考えてみます。

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

コンパイラは自然な配置を強制するようにこの構造体を埋めます。

次のコード例は、コンパイラがメモリに埋め込みの構造体を配置する方法を示しています。

// 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];

両方の宣言は、sizeof(struct x_) を 12 バイトとして返します。

2 番目の宣言には、埋め込みの 2 つの要素が含まれています。

  1. char _pad0[3]int b のメンバーを 4 バイト境界上に配置します。
  2. char _pad1[1] 構造体の配列要素を struct _x bar[3]; 4 バイト境界に配置します。

埋め込みは自然なアクセスを可能にする方法でbar[3] の要素を配置します。

bar[3] 配列のレイアウトを次のコード サンプルに示します。

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 および alignas

alignas指定子は、変数とユーザー定義型のカスタム配置を指定する移植可能な C++ 標準の方法です。 alignof 演算子も同様に、指定された型または変数の配置を取得する、標準的で移植可能な方法です。

クラス、構造体つまり共用体、または個別のメンバーに対して alignas を使用できます。 複数の alignas 指定子が検出されると、コンパイラは最大値を持つ指定子を選択します。

// 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
}

関連項目

データ構造体の配置