align (C++)

Visual Studio 2015 以降では、指定子 (C++11) を使用alignasして配置を制御します。 詳細については、「Alignment」を参照してください。

Microsoft 固有の仕様

ユーザー定義データ (関数の静的割り当てや自動データなど) のアラインメントを正確に制御するには、__declspec(align(#)) を使用します。

構文

__declspec( align(#) )declarator

解説

最新のプロセッサ命令を使用するアプリケーションの記述では、いくつかの新しい制約や問題があります。 多くの新しい命令では、データを 16 バイトの境界に揃える必要があります。 頻繁に使用されるデータをプロセッサのキャッシュ ライン サイズに合わせて調整すると、キャッシュのパフォーマンスが向上します。 たとえば、サイズが 32 バイト未満の構造体を定義する場合は、32 バイトのアラインメントを使用して、その構造体の種類のオブジェクトが効率的にキャッシュされるようにすることができます。

# は配置値です。 有効なエントリは、1 ~ 8192 (バイト) の 2 の整数乗 (2、4、8、16、32、64 など) です。 declarator は、aligned として宣言するデータです。

型のアラインメント要件である size_t 型の値を返す方法については、「alignof」を参照してください。 64 ビット プロセッサを対象としたときにアラインされていないポインターを宣言する方法については、「__unaligned」を参照してください。

__declspec(align(#)) は、structunion、または class を定義するときや、変数を宣言するときに使用できます。

コンパイラは、コピー操作またはデータ変換操作中にデータのアラインメント属性を保証したり、保持しようとしたりしません。 たとえば、memcpy は、__declspec(align(#)) で宣言された構造体を任意の場所にコピーできます。 通常のアロケーター (たとえば、 mallocC++ operator new、Win32 アロケーター) は、構造体または構造体の配列に対して __declspec(align(#)) アラインされていないメモリを返します。 コピーまたはデータ変換操作の同期先が正しくアラインされるようにするには、_aligned_malloc を使用します。 または、独自のアロケーターを記述します。

関数パラメーターのアラインメントは指定できません。 スタック上の値で配置属性を持つデータを渡すと、呼び出し規則によってそのアラインメントが制御されます。 呼び出された関数でデータのアラインメントが重要な場合は、使用する前にパラメーターを正常にアラインされたメモリにコピーします。

__declspec(align(#)) がない場合、コンパイラはターゲット プロセッサおよびデータのサイズに基づいて、自然な境界にデータをアラインします。32 ビット プロセッサでは 4 バイト境界、64 ビット プロセッサでは 8 バイト境界にアラインします。 クラスまたは構造体のデータは、自然なアラインメントと現在のパッキング設定 (#pragma pack または /Zp コンパイラ オプションから) のうちの最小値でクラスまたは構造体内でアラインされます。

__declspec(align(#)) を使用する例を次に示します。

__declspec(align(32)) struct Str1{
   int a, b, c, d, e;
};

この型には、32 バイト アラインメント属性が設定されました。 これは、すべての静的および自動インスタンスが 32 バイト境界で開始されることを意味します。 この型をメンバーとして宣言された他の構造体型では、この型の配置属性が保持されます。 つまり、要素として持つ Str1 構造体には、少なくとも 32 のアラインメント属性があります。

ここでは、sizeof(struct Str1) は 32 です。 このため、Str1 オブジェクトの配列が作成されて、配列のベース アドレスが 32 バイトでアラインされる場合、配列の各メンバーも 32 バイトでアラインされます。 ベースが動的メモリ内で正しくアラインされている配列を作成するには、_aligned_malloc を使用します。 または、独自のアロケーターを記述します。

構造体の sizeof 値は、最後のメンバーのオフセットにメンバーのサイズを加え、最大サイズのメンバーのアラインメント値の倍数か、構造体全体のアラインメント値のどちらか大きい方に切り上げた値です。

コンパイラは、構造体のアラインメントにこれらの規則を使用します。

  • __declspec(align(#)) でオーバーライドしない限り、スカラー構造体のメンバーのアラインメントは、そのサイズと現在のパッキング設定の最小値になります。

  • __declspec(align(#)) でオーバーライドされない限り、構造体のアラインメントは、そのメンバーの個々のアラインメントの最大値になります。

  • 構造体のメンバーが配置される、親の構造体の先頭からのオフセットは、アラインメントの最小倍数であり、かつ前のメンバーの最後のオフセットに等しいかそれよりも大きくなります。

  • 構造体のサイズは、アラインメントの最小倍数であり、かつ最後のメンバーの最後のオフセットに等しいかそれよりも大きくなります。

__declspec(align(#)) では、単にアラインメントの制限が多くなることがあります。

詳細については、以下を参照してください:

align 使用例

次の例では、__declspec(align(#)) がデータ構造体のサイズとアラインメントにどのような影響を与えるかを示します。 この例では、次の定義を前提とします。

#define CACHE_LINE  32
#define CACHE_ALIGN __declspec(align(CACHE_LINE))

次の例では、S1 構造体は __declspec(align(32)) を使用して定義されます。 変数定義または他の型宣言で S1 を使用すると、すべて 32 バイトでアラインされます。 sizeof(struct S1) は 32 を返し、S1 では 4 つの整数の保持に必要な 16 バイトの後に 16 バイトのパディングが挿入されます。 各 int メンバーには 4 バイトのアラインメントが必要ですが、構造体自体のアラインメントは 32 として宣言されています。 そのため、全体的なアラインメントは 32 になります。

struct CACHE_ALIGN S1 { // cache align all instances of S1
   int a, b, c, d;
};
struct S1 s1;   // s1 is 32-byte cache aligned

次の例では、sizeof(struct S2) は 16 を返します。これはメンバーのサイズの合計と一致します。この値が最大アラインメント要件の倍数 (8 の倍数) になっているためです。

__declspec(align(8)) struct S2 {
   int a, b, c, d;
};

次の例では、sizeof(struct S3) は 64 を返します。

struct S3 {
   struct S1 s1;   // S3 inherits cache alignment requirement
                  // from S1 declaration
   int a;         // a is now cache aligned because of s1
                  // 28 bytes of trailing padding
};

次の例では、a で自然なアラインメントが行われる (この場合は 4 バイトにアラインされる) ことに注意してください。 ただし、S1 は 32 バイトでアラインする必要があります。 a がオフセット 32 で開始するように、s1 の次の 28 バイトがパディングされます。 次に、S4S1 のアラインメント要件を継承します。それが構造体に必要な最大アラインメントであるためです。 sizeof(struct S4) は 64 を返します。

struct S4 {
   int a;
   // 28 bytes padding
   struct S1 s1;      // S4 inherits cache alignment requirement of S1
};

次の 3 つの変数宣言も __declspec(align(#)) を使用します。 それぞれの場合で、変数は 32 バイトでアラインする必要があります。 配列では、配列の各メンバーではなく、配列のベース アドレスが 32 バイトでアラインされます。 配列の各メンバーの sizeof 値は __declspec(align(#)) の使用による影響を受けません。

CACHE_ALIGN int i;
CACHE_ALIGN int array[128];
CACHE_ALIGN struct s2 s;

配列の各メンバーをアラインするには、次のようなコードを使用する必要があります。

typedef CACHE_ALIGN struct { int a; } S5;
S5 array[10];

次の例では、構造体自体のアラインメントと最初の要素のアラインメントによる影響が同じであることに注意してください。

CACHE_ALIGN struct S6 {
   int a;
   int b;
};

struct S7 {
   CACHE_ALIGN int a;
               int b;
};

S6S7 では、アラインメント、割り当て、サイズの属性が同じです。

この例では、開始アドレスのa配置は、bcdそれぞれ 4、1、4、および 1 です。

void fn() {
   int a;
   char b;
   long c;
   char d[10]
}

メモリがヒープ上に割り当てられる場合のアラインメントは、どの割り当て関数が呼び出されるかによって異なります。 たとえば、malloc を使用する場合、結果はオペランドのサイズによって決まります。 arg>= 8 の場合、返されるメモリは 8 バイトアラインされます。 arg< 8 の場合、返されるメモリのアラインメントは arg より小さい最初の 2 の累乗値になります。 たとえば、malloc(7) を使用すると、アラインメントは 4 バイトになります。

__declspec(align(#)) を使用した新しい型の定義

型のアラインメントを定義できます。

たとえば、次のように struct のアラインメント値を定義できます。

struct aType {int a; int b;};
typedef __declspec(align(32)) struct aType bType;

これで、aTypebType は同じサイズ (8 バイト) ですが、bType 型の変数は 32 バイトでアラインされます。

スレッド ローカル ストレージでのデータのアライン

__declspec(thread) 属性を使用して作成され、イメージ内の TLS セクションに配置された静的なスレッド ローカル ストレージ (TLS: Thread-Local Storage) は、通常の静的データとまったく同じようにアラインメントされます。 TLS データを作成するために、オペレーティング システムは、TLS セクションのサイズのメモリを割り当て、TLS セクションのアラインメント属性に従います。

次の例では、アラインされたデータをスレッド ローカル ストレージに配置するさまざまな方法を示します。

// put an aligned integer in TLS
__declspec(thread) __declspec(align(32)) int a;

// define an aligned structure and put a variable of the struct type
// into TLS
__declspec(thread) __declspec(align(32)) struct F1 { int a; int b; } a;

// create an aligned structure
struct CACHE_ALIGN S9 {
   int a;
   int b;
};
// put a variable of the structure type into TLS
__declspec(thread) struct S9 a;

データのパッキングでの align の動作方法

/Zp コンパイラ オプションおよび pack プラグマは、構造体メンバーおよび共用体メンバーのデータ パッキングに影響を与えます。 この例では、/Zp__declspec(align(#)) がどのように連携するかを示しています。

struct S {
   char a;
   short b;
   double c;
   CACHE_ALIGN double d;
   char e;
   double f;
};

次の表に、さまざまな /Zp (または #pragma pack) 値での各メンバーのオフセットと共に、それらの値とオフセットの関係を示します。

変数 /Zp1 /Zp2 /Zp4 /Zp8
a 0 0 0 0
b 1 2 2 2
c 3 4 4 8
d 32 32 32 32
e 40 40 40 40
f 41 42 44 48
sizeof(S) 64 64 64 64

詳細については、「/Zp (構造体メンバーのアラインメント)」を参照してください。

オブジェクトのオフセットは、前のオブジェクトのオフセットと現在のパッキング設定に基づきます。ただし、オブジェクトに __declspec(align(#)) 属性が設定されていない場合です。その場合は、アラインメントは前のオブジェクトのオフセットとオブジェクトの __declspec(align(#)) 値に基づきます。

Microsoft 固有の仕様はここまで

関連項目

__declspec
ARM ABI 規則の概要
x64 ソフトウェア規約