第16章の2 union(共用体)

union(共用体)は、同一のデータ領域を複数個の異なるデータ型が共用するようにしたものです。

1. 共用体の概念

共用体は、宣言の「struct」が「union」になるだけで、それ以外は宣言の仕方や使い方などは構造体と全く同じです。 しかし、共用体は各メンバがすべて同じアドレスから割り振られている点が構造体とは異なります。

たとえば、smpl という共用体タグ名で次のような共用体を宣言すると、メモリ上は図のようになります。

2. 共用体の使用手順

共用体の使用手順は構造体と変わりありません。以下の手順で行います。

  1. 共用体の型枠の宣言
  2. 共用体の宣言(メモリ上に領域を確保)
  3. 初期化
  4. 参照

ただし、初期化には注意が必要です。 共用体の初期化はその最初のメンバの値で行わなければなりません

#include <stdio.h>

// 共用体の型枠の宣言
union smpl {
    int   i;
    short s;
    char  c;
};

int main(void)
{
    // 共用体の宣言と初期化
    // (1) : int 型メンバ i への初期化/
    union smpl dt = { 0x11111111 };

    // 共用体の参照
    printf("dt.i = 0x%x\n",   dt.i);
    printf("dt.s = 0x%x\n",   dt.s);
    printf("dt.c = 0x%x\n",   dt.c);

    dt.c = 0x22;    // (2)
    printf("\n");
    printf("dt.i = 0x%x\n",   dt.i);
    printf("dt.s = 0x%x\n",   dt.s);
    printf("dt.c = 0x%x\n",   dt.c);

    dt.s = 0x3333;  // (3)
    printf("\n");
    printf("dt.i = 0x%x\n",   dt.i);
    printf("dt.s = 0x%x\n",   dt.s);
    printf("dt.c = 0x%x\n",   dt.c);

    dt.i = 0x44444444;  // (4)
    printf("\n");
    printf("dt.i = 0x%x\n",   dt.i);
    printf("dt.s = 0x%x\n",   dt.s);
    printf("dt.c = 0x%x\n",   dt.c);

    return 0;
}

※ 配置は処理系によって異なります。

【実行結果例】
dt.i = 0x11111111
dt.s = 0x1111
dt.c = 0x11

dt.i = 0x11111122
dt.s = 0x1122
dt.c = 0x22

dt.i = 0x11113333
dt.s = 0x3333
dt.c = 0x33

dt.i = 0x44444444
dt.s = 0x4444
dt.c = 0x44

  • 共用体では、最後に書き込みを行ったメンバのサイズに応じて下位(あるいは上位)ビットが更新され、それを他のサイズのメンバで読むと「下位何ビットかを切り出した」形で値が見えます。
  • たとえば、32ビット幅の int に書き込めば全4バイトが決まりますが、16ビットの short で読むと「下位2バイト分(下位16ビット)」、8ビットの char で読むと「下位1バイト分(下位8ビット)」が表示される、というわけです。

3. 共用体を含む構造体

たとえば、ある構造を持つデータがあるとして、ある条件のときのみデータの一部を変更したいとします。 単に構造体のみでデータを扱う場合には、条件によって 2つの構造体を作ることになりますが、構造体の中に共用体を含むようにすれば、構造体は 1つで済みます。

【例】
次のように選択種別によって異なる成績データをもつ学生群 1 と 2 とがある。
これらを構造体の中に共用体を含むことにより、 1つのデータ構造で宣言することができる。

#include <stdio.h>

struct kamoku1 {        // 選択科目1
    int kokugo;
    int eigo;
    int syakai;
};
struct kamoku2 {        // 選択科目2
    int suugaku;
    int eigo;
};
struct seiseki {   // 成績
    int no;           // 学生番号
    char name[40];    // 氏名
    int sentaku;      // 選択種別
    union kamoku{     // 選択科目
        struct kamoku1 k1;
        struct kamoku2 k2;
    }ka;
    double heikin;    // 平均値
};

int main(void)
{
    struct seiseki mycls[20] = {
        { 1001, "小柳幸江", 1, 76, 87, 69 },
        { 1002, "大場優", 2, 79, 48, 0 },
        { 1003, "新庄あやか", 2, 85, 98, 0 },
        { 1004, "野崎栄一", 1, 43, 76, 56 },
        { 1005, "本多健也", 1, 69, 91, 69 },
        { 9999, "" , 0, 0, 0, 0 },
    };
    int i = 0;
    
    while (mycls[i].no != 9999) {
        printf("%4d %-20s ", mycls[i].no, mycls[i].name);
        
        if (mycls[i].sentaku == 1) {
            printf("国語:%3d 英語:%3d 社会%3d\n",
             mycls[i].ka.k1.kokugo, mycls[i].ka.k1.eigo, 
             mycls[i].ka.k1.syakai);
        } else {
            printf("数学:%3d 英語:%3d\n",
             mycls[i].ka.k2.suugaku, mycls[i].ka.k2.eigo);
        }
        i++;
    }
    
    return 0;
}

【実行結果例】 1001 小柳幸江 国語: 76 英語: 87 社会 69 1002 大場優 数学: 79 英語: 48 1003 新庄あやか 数学: 85 英語: 98 1004 野崎栄一 国語: 43 英語: 76 社会 56 1005 本多健也 国語: 69 英語: 91 社会 69

構造体の中に共用体を含む場合、参照の際には面倒でも全てのタグとメンバをつなげて記述しなければなりません。

〇 演習問題

「3. 共用体を含む構造体」のサンプルプログラムを書き換えて、各学生の平均値を表示しなさい。

【実行結果例】 1001 小柳幸江 平均: 77.33 1002 大場優 平均: 63.50 1003 新庄あやか 平均: 91.50 1004 野崎栄一 平均: 58.33 1005 本多健也 平均: 76.33

解答例

#include <stdio.h>

struct kamoku1 {        // 選択科目1
    int kokugo;
    int eigo;
    int syakai;
};
struct kamoku2 {        // 選択科目2
    int suugaku;
    int eigo;
};
struct seiseki {   // 成績
    int no;           // 学生番号
    char name[40];    // 氏名
    int sentaku;      // 選択種別
    union kamoku{     // 選択科目
        struct kamoku1 k1;
        struct kamoku2 k2;
    }ka;
    double heikin;    // 平均値
};

int main( void )
{
    struct seiseki mycls[20] = {
        { 1001, "小柳幸江", 1, 76, 87, 69, 0.0 }, 
        { 1002, "大場優", 2, 79, 48, 0, 0.0 }, 
        { 1003, "新庄あやか", 2, 85, 98, 0, 0.0 }, 
        { 1004, "野崎栄一", 1, 43, 76, 56, 0.0 }, 
        { 1005, "本多健也", 1, 69, 91, 69, 0.0 }, 
        { 9999, "" , 0, 0, 0, 0, 0.0 },
    };
    int i = 0;

    while (mycls[i].no != 9999) {
        // 選択科目別に平均値を求める
        if (mycls[i].sentaku == 1) {
            mycls[i].heikin = (double)(mycls[i].ka.k1.kokugo 
                         + mycls[i].ka.k1.eigo
                         + mycls[i].ka.k1.syakai) / 3;
        }
        else {
            mycls[i].heikin = (double)(mycls[i].ka.k2.suugaku
                         + mycls[i].ka.k2.eigo) / 2;
        }
        // 結果の表示
        printf("%4d %-20s 平均:%6.2f\n", 
            mycls[i].no, mycls[i].name, mycls[i].heikin);
        i++;
    }

    return 0;
}

コメント