共用体(Union)とは
共用体とは、
プログラミング言語における
データ型の一つで、
同じメモリ領域を複数の異なるデータ型で共有する構造のことです。例えば、あるデータが数値の場合もあれば
文字列の場合もあるといった状況で、それぞれの
データ型用に別々のメモリ領域を用意する代わりに、一つの領域を共有することでメモリ使用量を節約できます。
共用体の基本的な考え方
共用体の本質は、メモリ領域を効率的に利用することにあります。例えば、数値か
文字列のどちらかしか格納しない場合、それぞれの型用にメモリを確保すると、一方の領域は常に無駄になります。そこで、一つの領域を複数の型で共有し、必要に応じて異なる型として解釈します。この共有された領域が、共用体です。
タグ付き共用体とタグなし共用体
共用体から意味のある値を取り出すには、格納されているデータの型を把握する必要があります。この型情報を
タグと呼びます。
- - タグ付き共用体(バリアント型): タグを付加情報として持ち、常に正しい型でデータを得られるように設計されています。
- - タグなし共用体: どの型のデータが格納されているかの管理は利用者に委ねられます。誤った型でアクセスすると、無意味な値や不正な値が得られる可能性があります。
ただし、意図的に異なる型として値を読み取ることで、バイト列を異なる
データ型として解釈するテクニックもあります。例えば、
整数型で格納されたデータを、より小さい
整数型としてアクセスし、上位/下位バイトを取り出すといった操作が可能です。ただし、このテクニックは
エンディアンなど環境に依存し、
移植性は低いという点に注意が必要です。
C言語では、タグなしの共用体がサポートされています。`union` キー
ワードを使用して宣言し、構造体と同様に `.` 演算子や `->` 演算子でメンバにアクセスできます。
共用体のサイズは、メンバの中で最大のものを格納できる大きさに決められ、初期化は先頭で宣言したメンバの型で行う必要があります。
また、構造体を含む共用体では、先頭メンバの型が同じ構造体が複数ある場合、それらの構造体の先頭メンバは正確に重なり合うことが保証されます。この仕様は、先頭メンバをタグ情報として使用する際に役立ちます。
C99までは無名の構造体や共用体が許可されていませんでしたが、C11で許可されるようになりました。
C++では、共用体はクラスの一種として扱われ、メンバ関数を持てるなどの機能が追加されています。しかし、普通のクラスに比べ以下のような制約があります。
- - 継承、基底クラス、派生ができない
- - 仮想関数や静的メンバを持てない
- - PODでないクラスオブジェクト、参照型をメンバにできない
これらの制約の一部は
C++11で撤廃されました。
C++では、共用体タグ名を省略して定義することで、スコープを形成しない無名の共用体を作成できます。ただし、無名の構造体は許可されていません。
制約のため、標準ライブラリを含め、ほとんどのクラスは共用体に格納できません。これは、共用体がタグ情報を持たないために、コンストラクタやデストラクタを正しく実行するコードを自動生成できないからです。そのため、
C++で共用体が積極的に利用されることは少ないのが現状です。
C++では、バイト列の再解釈には `reinterpret_cast` が用いられることが多いです。
C#
C#には、共用体専用の構文は存在しません。ただし、`System.Runtime.InteropServices.StructLayoutAttribute` を使用して、明示的にレイアウトを指定した構造体で、共用体と同様の動作を実現できます。
まとめ
共用体は、メモリ効率を高める上で有用な機能ですが、データの型管理を適切に行う必要があります。特にタグなし共用体を使用する場合は、どの
データ型が格納されているかを常に把握し、誤った型でアクセスしないように注意が必要です。
C++のように共用体の利用が制限される言語もあるため、目的に応じて適切な方法を選択しましょう。
参考資料
- - ブライアン・カーニハン、デニス・リッチー (1988). The C Programming Language (2nd ed.) (K&R). Prentice Hall. ISBN 0-13-110362-8
- - ビャーネ・ストロヴストルップ (1994). The Design and Evolution of C++. Addison-Wesley. ISBN 0-201-54330-3
- - ビャーネ・ストロヴストルップ (2000). The C++ Programming Language. Addison-Wesley. ISBN 0-201-70073-5
関連項目