共用体(union)は、同じメモリ領域に異なる値を保存する特別なデータ型です。共用体では、すべてのメンバーが同じアドレスに配置され、メモリのサイズは最も大きなメンバーのサイズと等しくなります。
使用法:
例:
union Data { int i; float f; char s[4]; }; union Data d; d.i = 0x41424344; // メモリには現在4バイトがあり、int、float、文字列として読み取ることができる printf("%c%c%c ", d.s[0], d.s[1], d.s[2]); // ベンダー特有の出力
落とし穴と使用ルール:
質問: 共用体を使用する意味は何ですか?複数のフィールドを持つ構造体を単に使用できるのではないですか?
回答: 共用体の使用はメモリを節約します。なぜなら、いつでも内部にただ1つの値しか保持されないからです。構造体では、各フィールドにメモリが割り当てられますが、共用体では最大のフィールドに対してのみメモリが割り当てられ、残りはそのメモリを「共有」します。また、共用体は同じメモリのフラグメントでの異なるデータ表現間での安全または意図的な変換を可能にします。
例:
struct S { int i; float f; } s; // sizeof = sizeof(int) + sizeof(float) union U { int i; float f; } u; // sizeof = max(sizeof(int),sizeof(float))
物語
デバイスドライバプロジェクトで、データへのビットアクセスを使用した共用体を介して「ハードウェア」との通信が行われていました。小さなリファクタリングの後、開発者は共用体の異なるフィールドの1つに書き込むことを忘れ、不正確なデータの読み取りや致命的なシステムクラッシュを引き起こしました。共用体では、いつでも「本物」であるフィールドは1つだけです。
物語
ネットワークパケットを共用体を介して交換する際、メモリ管理を行っていた開発者が構造体のアライメントを考慮するのを忘れました。その結果、1バイトのオフセットが発生し、構造体が不正なオフセットで解析され、プロトコルがオリジナルと互換性がなくなりました。
物語
シリアル化ライブラリの作成中に、プログラマーは共用体を値として関数に渡しましたが、読み取りの前に必要なフィールドが初期化されていませんでした。そのため、データが不正にシリアル化され、出力ストリームにゴミが発生し、元の情報の復元が不可能になりました。