同じメモリ領域に別の変数名を定義し、使用出来るようにしたデータ構造。 考え方はCOBOLのREDEFINESに似ているが、実際は、違う点も多いので要注意。
C言語って、「こういう書き方も出来る」とか「こういう考え方もある」とか、いっぱい有るので、
とりあえず、ざっくりと。
またこのページにおいては、
char型は1語、short型は2語、int型は4語、とします。
構造体の場合は、変数は、順に別のアドレスに割り当てられます。
struct st { int i; char c; }d;
目印d V |d.i |d.c| -+---+---+---+---+---+- | | | | | | -+---+---+---+---+---+-
共用体の場合は、変数は、全て共用体の先頭アドレスから割り当てられます。
union uni { int i; char c; }d;
目印d V |d.i | |d.c| -+---+---+---+---+---+- | | | | | | -+---+---+---+---+---+-
共用体と構造体を組み合わせると、こんなことも出来ます。
union uni { int i; struct st { short s; char c1; char c2; }sd; }d;
目印d V |d.i | |d.sd.s |d.sd.c1|d.sd.c2| -+-------+-------+-------+-------+- | | | | | -+-------+-------+-------+-------+-
共用体は、 同じメモリ領域で色々な切り分けが可能となりますが、 当然、記憶されている値は1つなので、物凄く注意が必要です。
以下は、4つの番地(0018FF4C~0018FF4F番地)に記憶されている32ビット分の数値を、
16ビットのshort型変数2つ分、および、
8ビットのchar型変数4つ分として見た結果を表示するプログラム。
「little endian」は、複数の連続した番地で1つの値を記憶する時の手法の一つで、
値の上位桁を番地の大きい方に記憶する。
乱暴なイメージで言えば、十進数の各桁の千百十一の並びが逆になっている様なもの。
/* RynUnion3.c 2013.8.30 */ #include <stdio.h> struct st1 { unsigned short s; }; struct st2 { unsigned char c1; unsigned char c2; }; union uni { struct st1 sdata; struct st2 cdata; }; int main(void) { union uni data[2] = {260, 12345}; int j; printf("¥n"); printf("short型(16bit)では¥n¥n"); for(j = 0; j < 2; j++ ){ printf("data[%d].sdata.s = %p番地 : %5d ( 0x%04x )¥n", j, &data[j].sdata.s, data[j].sdata.s, data[j].sdata.s); } printf("¥n"); printf("char型(8bit)では¥n¥n"); for(j = 0; j < 2; j++ ){ printf("data[%d].cdata.c1 = %p番地 : %3d ( 0x%02x )¥n", j, &data[j].cdata.c1, data[j].cdata.c1, data[j].cdata.c1); printf("data[%d].cdata.c2 = %p番地 : %3d ( 0x%02x )¥n", j, &data[j].cdata.c2, data[j].cdata.c2, data[j].cdata.c2); } printf("little endian なので、¥n"); for(j = 0; j < 2; j++ ){ printf("data[%d].cdata.c2 × 256 + data[%d].cdata.c1 = ", j, j); printf("%2d x 256 + %2d = %5d¥n", data[j].cdata.c2, data[j].cdata.c1, data[j].cdata.c2*256+data[j].cdata.c1); } printf("¥n"); return 0; }
COBOLっぽい使い方の例。電話番号を区切ってます。
勿論、実際にはもっと複雑なことをしているのだろうけれど、
サンプルですから。
/* RynUnion2a.c 2013.08.30 */ #include <stdio.h> struct st1 { /* 0x-xxxx-xxxx */ char shigai[1]; char shinai[4]; char bangou[4]; }; struct st2 { /* 0xx-xxx-xxxx */ char shigai[2]; char shinai[3]; char bangou[4]; }; struct st3 { /* 0xxx-xx-xxxx */ char shigai[3]; char shinai[2]; char bangou[4]; }; struct meibo { int no; char name[10]; union tel { char number[9]; struct st1 t1; struct st2 t2; struct st3 t3; }denwa; }; int main(void) { struct meibo home11[10] = { { 1001, "atou", "31234xxxx" }, { 1002, "itou", "45123xxxx" }, { 1003, "udou", "12345xxxx" }, { 9999, "", "" }, }; int i = 0; printf("phone number¥n"); while (home11[i].no != 9999) { printf("%4d %-6s", home11[i].no, home11[i].name); switch(home11[i].denwa.number[0]){ case '3': printf("+81 %.1s-%.4s-%.4s¥n", home11[i].denwa.t1.shigai, home11[i].denwa.t1.shinai, home11[i].denwa.t1.bangou); break; case '4': printf("+81 %.2s-%.3s-%.4s¥n", home11[i].denwa.t2.shigai, home11[i].denwa.t2.shinai, home11[i].denwa.t2.bangou); break; default: printf("+81 %.3s-%.2s-%.4s¥n", home11[i].denwa.t3.shigai, home11[i].denwa.t3.shinai, home11[i].denwa.t3.bangou); break; } i++; } return 0; }
場合によって数値と文字を使い分けたい時の例。
かなり、無理矢理。
共用体の初期化は、
定義の一番上に書いたデータ型に決められてしまうので、
プログラム中で代入しています。
/* RynUnion4.c 2013.08.30 */ #include <stdio.h> #include <string.h> struct st { int no; char name[10]; int flg; union uni { int i; char c[5]; }seiseki; }; int main(void) { int j = 0; struct st data[10]; data[0].no = 1101; strcpy(data[0].name,"阿藤"); data[0].flg = 0; data[0].seiseki.i = 95; data[1].no = 1102; strcpy(data[1].name,"伊藤"); data[1].flg = 1; strcpy(data[1].seiseki.c,"欠席"); data[2].no = 1103; strcpy(data[2].name,"有働"); data[2].flg = 0; data[2].seiseki.i = 100; data[3].no = 1104; strcpy(data[3].name,"江藤"); data[3].flg = 0; data[3].seiseki.i = 90; data[4].no = 1105; strcpy(data[4].name,"加藤"); data[4].flg = 1; strcpy(data[4].seiseki.c,"休学"); data[5].no = 9999; strcpy(data[5].name,""); data[5].flg = 0; data[5].seiseki.i = 0; printf("¥n"); while (data[j].no != 9999) { printf("%4d %s", data[j].no, data[j].name); switch(data[j].flg){ case 0: printf(" %3d¥n", data[j].seiseki.i); break; case 1: printf(" %4s¥n", data[j].seiseki.c); break; default: break; } j++; } return 0; }