Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

なぜC/C++の構造体のsizeofは各メンバーのサイズの合計と一致しないのか?パディングとアライメントの仕組みを解説

sizeof() 演算子で取得できる構造体のサイズは、必ずしも各メンバーのサイズの合計と一致するとは限りません。コンパイラは、メモリのアライメント(整列)に関する問題を回避するために、メンバーの間や構造体の末尾にパディング(埋め草バイト)を自動的に挿入することがあります。その結果、構造体全体のサイズは見かけの合計よりも大きくなるのです。

パディングが挿入される代表的なケースは次のとおりです。

  • あるメンバーの直後に、より大きなアライメント要件を持つメンバーが続くとき
  • 構造体全体のサイズを最大アライメント値の倍数に揃えるため、末尾に追加されるとき

アライメントの制約はコンパイラによって異なり、C標準では具体的な整列方法は処理系(実装)に依存すると定められています。以下では、メンバーの宣言順序を変えた3つのケースを通じて、実際にサイズがどう変わるかを確認します。ここでは一般的な環境を想定し、int は4バイト、double は8バイト、short は2バイトとして説明します。

ケース1:int → double → short の順

このケースでは、double 型の z は8バイトあり、直前の x(4バイト)より大きいため、x の後ろに4バイトのパディングが挿入されます。さらに short 型の y 自体は2バイトですが、構造体全体のサイズを8バイトの倍数に揃える必要があるため、末尾にも6バイトのパディングが追加されます。

なぜC/C++の構造体のsizeofは各メンバーのサイズの合計と一致しないのか?パディングとアライメントの仕組みを解説

サンプルコード

#include <stdio.h>

struct myStruct {
    int x;       // int型は4バイト。doubleのアライメントのため4バイトのパディング
    double z;    // double型は8バイト。パディングなし
    short int y; // short型は2バイト。末尾に6バイトのパディング
};

int main(void) {
    printf("Size of struct: %d", (int)sizeof(struct myStruct));
    return 0;
}

実行結果

Size of struct: 24

ケース2:double → int → short の順

今度は最大の double 型を先頭に配置します。z が先頭の8バイトを占有し、続く int 型の x(4バイト)はそのまま隣接して配置できます。最後の short 型の y(2バイト)も同様に配置でき、末尾に2バイトのパディングを足すだけで済みます。その結果、全体で16バイトとなりました。

なぜC/C++の構造体のsizeofは各メンバーのサイズの合計と一致しないのか?パディングとアライメントの仕組みを解説

サンプルコード

#include <stdio.h>

struct myStruct {
    double z;    // double型は8バイト。パディングなし
    int x;       // int型は4バイト。そのまま配置
    short int y; // short型は2バイト。末尾に2バイトのパディング
};

int main(void) {
    printf("Size of struct: %d", (int)sizeof(struct myStruct));
    return 0;
}

実行結果

Size of struct: 16

ケース3:double → short → int の順

このケースも合計サイズは16バイトですが、内部の配置はケース2とは異なります。先頭の double の後に short を置くと、次の int は4バイト境界に揃う必要があるため、short の直後に2バイトのパディングが入ります。その後 int が配置され、合計がすでに8の倍数(16バイト)となるため、末尾に追加のパディングは不要です。

なぜC/C++の構造体のsizeofは各メンバーのサイズの合計と一致しないのか?パディングとアライメントの仕組みを解説

サンプルコード

#include <stdio.h>

struct myStruct {
    double z;    // double型は8バイト。パディングなし
    short int y; // short型は2バイト。intのアライメントのため2バイトのパディング
    int x;       // int型は4バイト。パディングなし
};

int main(void) {
    printf("Size of struct: %d", (int)sizeof(struct myStruct));
    return 0;
}

実行結果

Size of struct: 16

まとめ

構造体のサイズは「各メンバーの合計 + パディング」となります。一般に、サイズの大きいメンバーから順に宣言することでパディングを最小限に抑えられ、メモリ使用量を削減できます。

なお、#pragma pack(1) などでアライメントを強制的に解除してパディングをなくすことも可能ですが、環境によっては未整列アクセスによる性能低下やクラッシュを招くことがあります。利用する際は、対象プラットフォームの特性を十分に理解した上で慎重に行いましょう。

  1. 三角マッチ棒数を求めるC/C++プログラムの解説と実装例

    三角マッチ棒数とはマッチ棒を正三角形の形に並べて作った三角形のことを「三角マッチ棒数(Triangular Matchstick Number)」と呼びます。三角マッチ棒数とは、そのマッチ棒の三角形を作るために必要なマッチ棒の本数を指します。問題の概要この問題では、マッチ棒で作るピラミッドの段数 X が与えられます。そして、X 段のマッチ棒ピラミッドを構成するために必要なマッチ棒の最小総本数を出力するプログラムを作成するのが課題です。概念をより明確にするために、具体例を見てみましょう。入力: 7 出力: 84解法の考え方この問題は、三角数(Triangular Number)の拡張として考える

  2. C/C++プログラムで放物線の頂点・焦点・準線を求める方法

    この記事では、C言語またはC++のプログラムを使って、放物線の頂点・焦点・準線(ダイレクトリックス)を求める方法を解説します。これらのパラメータを計算するには、放物線の一般方程式が必要です。一般式は次のとおりです。y = ax2 + bx + cここで、係数 a、b、c の値はあらかじめ与えられているものとします。各パラメータを求める公式頂点(Vertex)放物線の頂点は、次の座標で表されます。x = −b / (2a)y = (4ac − b2) / (4a)焦点(Focus)焦点は、頂点より 1/(4a) だけ上に位置します(a > 0 の場合)。x = −b / (2a)y = (