C言語では、構造体(struct)は他の構造体や異なるデータ型を含むことができます。しかし、構造体内のフィールドの配置は、そのサイズとアラインメントに影響を与えることがあります。コンパイラは、最大のフィールドに合わせてアラインメントを保つために追加のバイト(パディング)を追加します。これはパフォーマンスとプロセッサの正しい動作にとって重要です。
構造体がネストされた構造体を含む場合、アラインメントは再帰的に適用されます:
#pragma pack やGCC/Clangの __attribute__((packed)))を使用する。例:
#include <stdio.h> struct Inner { char c; int x; }; struct Outer { char a; struct Inner b; short s; }; int main() { printf("sizeof(struct Inner) = %zu\n", sizeof(struct Inner)); printf("sizeof(struct Outer) = %zu\n", sizeof(struct Outer)); return 0; } // sizeof(struct Inner) = 8 (64ビットの場合)、sizeof(struct Outer) = 16または24
packとpackedを使用すると、アラインメントを無視することによってサイズが減少しますが、これは一部のプラットフォーム(ARM、DSPなど)でパフォーマンスを低下させたりエラーを引き起こす可能性があります。
質問: 同じ構造体のサイズがすべてのプラットフォームで同じであることを保証できますか?
回答: いいえ!C標準は同じアラインメントやバイトオーダー(エンディアン)を保証せず、フィールドの間や構造体の末尾にパディングを「追加」する可能性があります。構造体のバイナリ互換性を保つためには、アラインメントの手動管理を使用し、常に対象プラットフォームごとにsizeofを明示的にテストしてください。
例:
struct Data { char c; int x; }; // sizeof(Data)は通常64ビットで8、32ビットで5または8
歴史
マイクロコントローラ(ARM)とPC間でバイナリプロトコルを介して構造体を交換する際、ARMから受信したデータがPCで期待されるものと一致しなかったのは、異なるパディングとバイト順によるものでした。その結果、誤ったパラメータがデコードされ、デバイス制御に物理的なエラーが生じました。
歴史
ネットワークプロトコルでデータ送信の高速化のためにpacked構造体が追加されましたが、アラインメントのないプラットフォームでは個別のフィールドが機能しないことを考慮していませんでした(プロセッサがミスアラインドアクセスをサポートしていなかったため)。結論:ネットワークパケット処理中のハードウェア例外。
歴史
サーバーは構造体のレコードから成る外部ファイルで動作していました。コンパイラを更新した後、構造体のサイズが変更され(異なるアラインメント)、古いデータがファイルから正しく読み込まれなくなり、一部の情報が「壊れて」しまいました。