同じメモリ領域に別の変数名を定義し、使用出来るようにしたデータ構造。 考え方は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;
}