言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

  1. トップページ
  2. C言語辞典
  3. 共用体(union)

共用体(union)

複数のメンバが同じメモリ領域を共有するデータ構造です。異なる型として同じデータを解釈したり、メモリを節約したりする目的で使用します。サイズは最大のメンバのサイズに等しくなります。

構文

// 共用体型を定義します。
union 共用体タグ名 {
    型 メンバ名1;
    型 メンバ名2;
};

// 共用体変数を宣言します。
union 共用体タグ名 変数名;

// メンバへのアクセス(構造体と同じ記法)。
変数名.メンバ名;
ポインタ->メンバ名;

// 初期化は最初のメンバに対してのみ行えます(C89)。
// C99以降は指定初期化子が使えます。
union 共用体タグ名 変数名 = {.メンバ名 = 値};

union と struct の比較

項目structunion
メモリ全メンバ分の合計(+パディング)を確保します。最大メンバ1つ分のサイズのみ確保します。
アクセス全メンバに独立してアクセスできます。有効なのは最後に書き込んだメンバのみです。
用途複数のデータをまとめて扱うことに使います。同じメモリを異なる型で解釈することに使います。

サンプルコード

#include <stdio.h>
#include <stdint.h>

// 数値を異なる型で解釈する共用体です。
union Number {
    int    i;
    float  f;
    unsigned char bytes[4];
};

// タグ付き共用体(どのメンバが有効かをenumで管理します)。
typedef enum { TYPE_INT, TYPE_DOUBLE, TYPE_STRING } ValueType;

typedef struct {
    ValueType type;
    union {
        int    i_val;
        double d_val;
        char   s_val[64];
    } data;
} Value;

void print_value(const Value *v) {
    switch (v->type) {
        case TYPE_INT:    printf("int: %d\n", v->data.i_val);    break;
        case TYPE_DOUBLE: printf("double: %f\n", v->data.d_val); break;
        case TYPE_STRING: printf("string: %s\n", v->data.s_val); break;
    }
}

int main(void) {
    union Number n;

    // int として書き込みます。
    n.i = 42;
    printf("int値: %d\n", n.i); // 『int値: 42』と出力されます。

    // float として書き込むと int の値は無効になります。
    n.f = 3.14f;
    printf("float値: %f\n", n.f);

    // バイト列として同じメモリを見ます(エンディアン確認に使われます)。
    n.i = 1;
    printf("bytes[0] = %u\n", n.bytes[0]); // リトルエンディアンなら『1』と出力されます。

    // サイズは最大メンバのサイズになります。
    printf("union Number のサイズ: %zu バイト\n", sizeof(union Number));

    // タグ付き共用体でバリアントデータを扱います。
    Value v1 = {TYPE_INT,    {.i_val = 100}};
    Value v2 = {TYPE_DOUBLE, {.d_val = 3.14}};
    print_value(&v1); // 『int: 100』と出力されます。
    print_value(&v2); // 『double: 3.140000』と出力されます。

    return 0;
}

概要

共用体の最大の特徴は、複数のメンバが同じメモリアドレスを共有することです。これにより1つの変数を複数の型として解釈できます。ハードウェアのレジスタやネットワークパケットの解析など、低レベルなバイト操作でよく使われます。

最後に書き込んだメンバ以外を読み出すことは、C言語の規格上「未定義動作」です。ただし、バイト表現の読み出し(『unsigned char』メンバ経由)は規格で許可されています。複数の型を安全に扱いたい場合はタグ付き共用体のパターンを使用してください。

関連するデータ構造として、全メンバを独立させたい場合は『struct(構造体)』を使います。関連する定数を列挙する場合は『enum(列挙型)』を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。