構造体を共用するときのデータ位置合わせの問題

一般に、最近のプロセッサー設計では、可能な限り最良のパフォーマンスを達成するために、メモリー内のデータをそれぞれの自然境界に位置合わせすることが必要とされます。 ほとんどの場合に、コンパイラーは、位置合わせ不良のデータの直前に埋め込みバイトを挿入することにより、位置合わせが正しくなるようにします。 このような埋め込みバイトはデータの整合性に影響を与えることはありませんが、予期しないレイアウトが生じた結果、構造体および共用体のサイズに影響することがあります。

pointer サイズと long サイズは、64 ビット・モードではどちらも 2 倍になるので、これらをメンバーとして含む構造体および共用体は、32 ビット・モードの場合より大きくなります。

重要: 表 1 での例は、例示目的でのみ使用しています。 32 ビット・プロセスと 64 ビット・プロセスの間でのポインターの共用は、誤った結果をもたらすことが多いので、お勧めできません
表 1. 32 ビット・プロセスと 64 ビット・プロセスの間でのポインターの共用
重要:
ソース:
#include <stdio.h>
#include <stddef.h>
int main()
{
    struct T {
        char c;
        int *p;
        short s;
        } t;
        printf("sizeof(t) = %d\n", sizeof(t));
        printf("offsetof(t, c) = %d sizeof(c) = %d\n",
    offsetof(struct T, c), sizeof(t.c));
    printf("offsetof(t, p) = %d sizeof(p) = %d\n",
    offsetof(struct T, p), sizeof(t.p));
    printf("offsetof(t, s) = %d sizeof(s) = %d\n",
    offsetof(struct T, s), sizeof(t.s));
}
ILP32 での出力:
sizeof(t) = 12
offsetof(t, c) = 0 sizeof(c) = 1
offsetof(t, p) = 4 sizeof(p) = 4
offsetof(t, s) = 8 sizeof(s) = 2
LP64 での出力:
sizeof(t) = 24
offsetof(t, c) = 0 sizeof(c) = 1
offsetof(t, p) = 8 sizeof(p) = 8
offsetof(t, s) = 16 sizeof(s) = 2
注:
  1. ILP32 でソースがコンパイルされ、実行されると、その結果は、メンバー Pの前、およびメンバー sの後に、パディングが挿入されたことを示します。 P がその本来の 4 バイト境界に位置合わせされるように、メンバー P の前に 3 つの埋め込みバイトが挿入されています。 構造体自体の位置合わせは、最も条件の厳しいメンバーの位置合わせと同じになります。 上記の例では、位置合わせの条件が最も厳しいのはメンバー p なので、構造体は 4 バイト境界で位置合わせされます。 構造体の末尾には、構造体の合計サイズを 4 バイトの倍数にするために、2 個の埋め込みバイトが挿入されます。 これが必要とされるのは、この構造体の配列を宣言する場合に、配列の各エレメントが適切に位置合わせされるようにするためです。
  2. LP64 の下でソースをコンパイルして実行する場合は、メンバー p を 8 バイトの自然位置合わせ境界に合わせるために、追加の埋め込みが必要になるので、構造体のサイズは 2 倍になります。

図 1 は、 表 1 に示されているソース・コードを、 ILP32 および LP64でコンパイラーがどのように扱うかを示しています。 ポインターはそれぞれの環境でサイズが異なるため、それぞれ異なる境界で位置合わせされます。 これは、コードが ILP32 と LP64の両方でコンパイルされている場合、位置合わせの問題が発生する可能性があることを意味します。 図 1 は、データの不整合の可能性を防ぐために、タイプ文字の埋め込みメンバーを定義するためのソリューションを示しています。 表 1 は、 表 1内のコードに必要な変更を示します。

表 1 内の構造体が 32 ビット・プロセスと 64 ビット・プロセスの間で共有または交換される場合、 図 1に示されているように、 1 つの環境のデータ・フィールド (および埋め込み) は、もう一方の環境の期待に一致しません。

図 1. 構造体が 32 ビット・プロセスと 64 ビット・プロセスの間で共用または交換される場合の位置合わせに関する潜在的問題の例.
この図は、コンパイラーが、ILP32 および LP64 の下で同じソース・コードをどのように取り扱うかを示しています。