C 言語での構造体の整列とパディングの使用

胡金庫 2023年10月12日
  1. C 言語での整列とパディングの基本を理解する
  2. メンバの並び替えテクニックを使用して C 言語のオブジェクトのスペースを節約する
C 言語での構造体の整列とパディングの使用

この記事では、C 言語で struct の整列とパディングを使用する方法をいくつか説明します。

C 言語での整列とパディングの基本を理解する

メモリ上のすべてのオブジェクトは、以下のような一次データ型で表現されます。charshortintlongpointer などです。これらのデータ型はメモリ上で対応するサイズを持っています。現代のほとんどの 64 ビットデスクトッププロセッサでは、char は 1 バイト、short は 2 バイト、int は 4 バイト、pointer は 8 バイト、などのサイズになっています。これらのサイズは保証されたものではないことに注意してください(char を除く)が、オブジェクトのサイズは sizeof 演算子を用いて取得できます。さて、アラインメントはコンパイラが変数をメモリに配置するために採用する方法であり、各基本データ型は対応するサイズで割り切れるアドレスに格納されることを意味しています。

通常、アラインメントはデータオブジェクトに高速かつ効率的にアクセスするために利用されます。アライメントは、連続的に宣言された異なるデータ型に対して、それらのアドレス間の間隔を強制的に含ませます。つまり、次の例に示すように、1つのポインタと char を持つ構造体 st1 を宣言すると、合計で 16 バイトを消費することになります。しかし、単一のポインタは 8 バイト、char は 1 バイトですから、st1 構造体は 9 バイトを占有しなければならないと考えるでしょう。しかし、すべてのメンバが最大のメンバサイズ(つまり 8 バイト)にアラインメントされているかのように動作します。st2 構造体は、7つの char メンバの配列を持つことを除いては、同じメモリ量を占有する同様の構造体を示しています。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
  typedef struct {
    char *p;
    char c2;
  } st1;

  typedef struct {
    char *p;
    char c2;
    char padding[7];
  } st2;

  printf("sizeof st1 = %zu\n", sizeof(st1));
  printf("sizeof st2 = %zu\n", sizeof(st2));

  exit(EXIT_SUCCESS);
}

出力:

sizeof st1 = 16
sizeof st2 = 16

メンバの並び替えテクニックを使用して C 言語のオブジェクトのスペースを節約する

前の例では、構造体が異なるタイプを含み、整列境界を埋めない場合にメモリの無駄があることを示しました。しかし、構造体のメンバを並べ替えて余分なスペースを節約できる場合もあります。

次のコード例では、真ん中に最大のメンバ (char *) を持つ foo1 構造体と、最初のメンバと同じメンバを持つ foo2 構造体を定義しています。この 2つのオブジェクトのサイズは 24 バイトと 16 バイトと異なっています。これはデータメンバの順序に関係しています。foo1 構造体では、p は 8 で割れるアドレスに整列する必要があるため、その前の intshort が合計 8 バイトを占め、その後の 2つの char * も 8 バイトを占めます。しかし、p を最初の位置に移動させれば、それに続くメンバが 8 バイトに収まるので、アライメントルールも満たされます。このようにして、foo2 のサイズは合計 16 バイトとなり、struct を詰めるように呼ばれることになります。gcc コンパイラは特別な __attribute__ ((packed)) 指定子を持っていて、順序のない struct メンバであっても強制的にパックさせることができることに注意してください。

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[]) {
  typedef struct {
    int n1;
    short s1;
    char *p;
    char c1;
    char c2;
  } foo1;

  typedef struct {
    char *p;
    int n1;
    short s1;
    char c1;
    char c2;
  } foo2;

  typedef struct {
    int n1;
    short s1;
    char *p;
    char c1;
    char c2;
  } __attribute__((packed)) foo3;

  printf("sizeof foo1 = %zu\n", sizeof(foo1));
  printf("sizeof foo2 = %zu\n", sizeof(foo2));
  printf("sizeof foo3 = %zu\n", sizeof(foo3));

  exit(EXIT_SUCCESS);
}

出力:

sizeof foo1 = 24
sizeof foo2 = 16
sizeof foo3 = 16
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C Struct