47章 共用体 union

http://www.geocities.jp/ky_webid/c/047.html

なんだかややこしそうな名前のものが出てきました。

実際にかなりややこしいです。

というか全部読んでみたのですが、いまいちどういうものかわかりませんでした。

その概念も難しい上に、これをどう活用するのかさえもイメージがつきません。

C言語を勉強し始めてからここまで理解できない概念にブチ当たったのは初めてです・・・。

ポインタ云々よりも共用体の方が難しくない?うーん。

もう少し実用的な使い方の項目で提示されている例もピンときません。

非常にまずいです。

ということで色々サイトを見て回って共用体について勉強してみました。

例えば、直線・円・円弧の3種類のデータをメモリー上に配列状に管理したいとします。とりあえず3種類の構造体を定義しますが、3つの配列でかまわないのならそれぞれを構造体の配列にします。

typedef struct{
    double sx,sy,ex,ey;
}LINE;
typedef struct{
    double cx,cy,r;
}CIRCLE;
typedef struct{
    int   direction;
    double sx,sy,ex,ey,cx,cy;
}ARC;

LINT  Line[100];
CIRCLE Circle[100];
ARC   Arc[100];

しかし、出来れば1つの配列で管理したい時にどうしますか?全てのメンバーの最小公倍数のような構造体を作っても出来そうですが、繁雑になります。ポインターの配列を作って実体を指すようにする方法もあります。これは最もメモリー効率も良さそうですが、メモリーを割り当てたり、開放したりの管理が大変そうです。こんな時に共用体が威力を発揮します。

typedef struct{
    int   type;  /* 0:line,1:circle,2:arc */
    union{
        LINE  l;
        CIRCLE c;
        ARC   a;
    }d;
}DATA;
DATA  DATA[300];
コンピューター:C言語講座:構造体・共用体・ビットフィールド


エクセレント。凄くよく意味が分かりました。

結局のところ、基本的にはメモリを確保する量を減らしたいときに使うわけですね。当然一度に扱うデータが一種類に限られてる場合ですが。

他にも

/*共用体のテストプログラム*/

#include <stdio.h>

struct c_byte {
  char low;
  char high;  };

/* 共用体宣言 */
union {
  short x;
  struct c_byte  xbyte;
  }  u_x;

main()
{
printf("16進数を入力(4ケタ) :");
scanf("%04x",&u_x.x);
printf("\n 入力された数(16進数) %04x",u_x.x);
printf("\n 上位バイト %02x  :  下位バイト %02x  ",u_x.xbyte.high,u_x.xbyte.low);
} 

実行例

A:\>union
16進数を入力(4ケタ) :3f7c

入力された数(16進数) 3f7c
上位バイト 3f : 下位バイト 7c
A:\>union
16進数を入力(4ケタ) :ff45

入力された数(16進数) ff45 
上位バイト ff : 下位バイト 45
A:\>

この例では共用体をあえて使う意義、便利さをそれほど感じないかもしれませんが、
とりあえず共用体とはこんなものというのは、おわかりいただけたとおもいます。

共用体

このような使い方もあるみたいです。

まだまだ実際にどう使うかまではピンときませんが、概念として凄くよく分かったので今の時点ではこれで良しとしておきます。

問題1

共用体を使って、short型の値を、long型に変換するプログラムを作って下さい。

・・・とりあえずやってみましょう。

int main (int argc,char *argv[]) {
    union {
        short num1;
        long num2;
    }u;
    u.num1 = 10;
    
    printf("%d",u.num2);
    return 0;
}

こういうことでしょうか?short型のnum1で代入した後、long型のnum2で参照すればlongとして値が取り出せる、というだけの話なのかな?ちょっと自信がありません。

答え合わせ

そういう意味であってましたね。

ただlong型のprintfする際に%ldにしないといけませんね。ちょっとミスってました。

問題2

共用体を使って、short型の値の、1バイト目と2バイト目とを入れ替えるプログラムを作って下さい。

これはさっきみてたサイトで似たような処理についての記述があったので若干カンニング気味になりました。

int main (int argc, char *argv[]) {
    union {
        short num;
        struct {
            char c1;
            char c2;
        }c;
    }u;
    char work;
    
    u.num = 10;
    
    printf("%d\n",u.num);
    
    work = u.c.c1;
    u.c.c1 = u.c.c2;
    u.c.c2 = work;
    
    printf("%d\n",u.num);
    return 0;
}
$ main
10
2560

多分これでうまくいってるとは思うのですが、出力結果が正しい結果なのか確認する手段がわからないのでちょっとヤキモキします。

答え合わせ

殆どあってましたね。僕の場合はstructで定義したんですが、解答例ではchar s[2]といったようにchar型の配列を用意するやり方でやっていますね。確かにその方が綺麗だと思いました。

問題3

次のプログラムの実行結果を答えて下さい。

union uTEST{
    struct STRUCT1{
        char str[4];
    }s1;
    struct STRUCT2{
        char c1;
        char c2;
        char c3;
        char c4;
    }s2;
    double d;
};

int main(void){
    union uTEST test;

    strcpy( test.s1.str, "abc" );
    test.s2.c2 = 'x';

    puts( test.s1.str );
    return 0;
}

見るからにややこしそう・・・。頑張って読み解いてみます。

s1のstrに"abc"を代入した後、s2のc2を'x'に書き換えています。

同じ領域を共有してるので2バイト目のデータが書き換わるということになります。

よって答えは「axc」になるはずです。

答え合わせ

あってました!いやーよかった。

ただdouble dを定義していたのは何故なんでしょう?使ってないですよね。単に混入してしまっただけなのかそれとも必要なのかちょっとわからなかったです。