キーワード辞典
かなり乱暴な共用体の話

登録日 13/08/31   更新日 13/08/31


共用体

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

実行結果





[ 赤い玉の画像 ] 「キーワード辞典」の目次へ

[ 黒板消しとチョーク受けの画像 ]