Share via


構造体のパッキングに関連する処理

2007/9/7

プログラマが意図したバイト数より多くのバイトが構造体で必要になった場合、特に所要領域が重要な場合は、問題が発生することがあります。

構造体のパッキングと配置

構造体のパッキングは、コンパイラの配置動作と次のように相互作用します。

  • パックサイズが既定の配置と同じかそれ以上に設定されている場合、パックサイズは無視されます。
  • パックサイズが既定の配置より小さく設定されている場合、コンパイラはそのパックサイズの値に従って配置を行います。

したがって、パックサイズが 4 に設定されている場合、サイズが 4、8、または 16 バイトのデータ型は 4 の倍数のアドレスに配置されます。ただし、サイズが 8 バイト (64 ビット) のデータ型が必ずしも 8 の倍数のアドレスに配置されるわけでははありません。パックサイズは構造体の外部のデータ型には影響を及ぼしません。

また、パッキングはパッキングされた構造体全体の配置に影響します。たとえば、#pragma pack(1) で宣言された構造体では、パッキングなしでも自然に配置されるかどうかにかかわらず、すべてのメンバの配置が強制的に 1 に設定されます。

次の手法では、パックサイズがバイトで設定されます。

  • コマンドライン オプション /Zp (構造体メンバの配置) では、パックサイズが n (1、2、4、8、または 16。既定は 8) に設定されます。**
  • コンパイラ ディレクティブ #pragma pack([n]) では、パックサイズが n (1、2、4、8、または 16) に設定されます。n を指定しないと、パックサイズは #pragma pack によってコンパイル開始時の値にリセットされます。コンパイル開始時の値とは、/Zp (構造体メンバの配置) によって指定した値か、または既定値 (ほとんどのプラットフォームで 8) です。
    pragma はソース内で出現した時点からのみ適用されます。たとえば、/Zp1 オプションではパックサイズが 1 に設定されますが、この場合、コンパイラは構造体内で埋め込みを使用しなくなります。この問題を回避するには、構造体のパッキングをオフにするか、またはポインタを使用してこのような構造体の非整列メンバにアクセスするときに __unaligned キーワードを使用します。

構造体のパッキングのガイドライン

次に、構造体の問題に関して考えられる解決策を示します。

  • 構造体メンバの並べ替え
    所要領域が重要な場合は、構造体のメンバを並べ替えて同じサイズの要素を隣り合わせにし、緊密にパッキングします。通常、サイズの大きいメンバから小さいメンバの順に並べます。
    構造体の並べ替えではユーザーがそのデータ構造体に対してフル コントロールを持つことが前提になりますが、ユーザーにメンバの並べ替えを実行する許可が与えられていない場合があります。たとえば、データ構造体がディスク上のファイル内のフィールドのレイアウトを表すことがあります。
    次にコード例を示します。

    struct x_
    {
       char a;     // 1 byte
       int b;      // 4 bytes
       short c;    // 2 bytes
       char d;     // 1 byte
    } MyStruct;
    

    この構造体のメンバを次のコード例のように並べ替えると、並べ替え後の構造体ではすべてのメンバが自然の境界に配置され、構造体のサイズは 12 バイトではなく 8 バイトになります。

    struct x_
    {
       int b;     // 4 bytes
       short c;   // 2 bytes
       char d;    // 1 byte
       char a;    // 1 byte
    } MyStruct;
    
  • 構造体への埋め込み
    各配列要素の配置が同じになるように構造体のサイズに埋め込みが必要なときに、ユーザーの側で配列要素間に埋め込みが行われないようにする必要がある場合は、別の種類の問題が発生することがあります。たとえば、メモリの使用量を制限する必要がある場合や、固定形式のソースからデータを読み取る必要がある場合です。
    構造体で埋め込みが必要な場合は、コンパイラ ディレクティブ #pragma pack を使用することができます。ただし、#pragma pack を使用した場合、構造体の要素は整列されません。また、__unaligned キーワード修飾子を使用して、配置エラーを発生させずにこのデータにアクセスするためのコードを生成する必要があります。
    次のコード例では、#pragma pack の使用により、ポインタ px が自然に配置されないデータを指していることをコンパイラに示すと共に、読み込み、マージ、および保存操作の適切なシーケンスを生成して配置を効率よく行うようコンパイラに指示しています。

    # pragma pack (1)
    struct x_
    {
       char a;    // 1 byte
       int b;     // 4 bytes
       short c;   // 2 bytes
    } MyStruct;
    # pragma pack ()
    
    void bar()
    {
      struct x_ __unaligned *px = &MyStruct;
       . . . .
       px->b = 5;
    }
    

    __unaligned キーワードは最後の手段としてのみ使用してください。生成されたコードでは、自然に配置されたデータにアクセスする場合より効率が悪くなるためです。ただし、配置エラーを発生させるよりは、__unaligned キーワードを使用した方がいいのは確かです。
    できる限り、データ構造体のメンバを並べ替えて配置を維持すると同時に、使用領域を最小限に抑えてください。

関連項目

参照情報

__unaligned キーワード