第16章 その他の型

第15章で構造体について説明しましたので、C言語初心者の方が学習すべき「データ型」についてはこれ以上は必要ないような気もします。 ただ、一部の入門書には「typedef」、「union」、「enum」についても説明がありますので、この章で取り上げたいと思います。


16−1.typedef

「typedef」は既にある型に対して新しい名前を作成するもので、次のように記述します。

typedef 既にある型 新しい名前;

たとえば、typedef がよく使われるものにビット列があります。 ビット演算をする場合には演算対象の変数は「unsigned(符号なし)」であることが条件ですが、このunsigned型をビット演算の対象である事を強調して次のように宣言したりします。

typedef unsigned char BYTE;	と宣言しておくと、このあとは
BYTE data;			と宣言できます。これは、
unsigned char data;		と宣言することと全く同じです。

また、第15章で学習した構造体では、頭に「struct」が付くのでどうしても型名が長くなります。 そのため、構造体の宣言を簡潔にするために、typedef は非常に多く用いられます。

15-1. 構造体の使用手順」で用いたプログラムを typedef を用いて書きなおすと次のようになります。

#include <stdio.h>

typedef struct {
	int no;		/* 学生番号 */
	char name[20];	/* 氏名 */
	double average;	/* 平均値 */
} SEISEKI;

int main(void)
{
	int i;
	SEISEKI seito1 = { 5, "KASAHARA", 83.5 }; 
	SEISEKI seito2[20] = {
		{ 1, "SAKURAI", 78.6 },
		{ 2, "NAGANO", 57.3 },
		{ 3, "TAKESHITA", 66.4 },
	};
	
	printf("%d %s %5.1f\n\n", seito1.no, seito1.name, seito1.average); 
	for (i = 0; i < 3; i++) {
		printf("%d %s %5.1f\n",
			seito2[i].no, seito2[i].name, seito2[i].average);
	}
	
	return 0;
}

【typedef 使用上の注意】

  1. typedef は新しい型を作り出すのではなく、既存の型に新しい名前を付け加えるだけである。
  2. #define」とよく似た機能であるが、#define はプリプロセッサにより解釈され、typedef はコンパイラにより解釈される。

    また、単に同義語を作る#defineに比べ、typedef は「型」の同義語を作るという目的が明確である。

  3. 宣言場所を注意しないと、他の関数から見えないことが起こる。
演習

16−2.union(共用体)

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

(1)共用体の概念

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

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

共用体の概念

(2)共用体の使用手順

共用体の使用手順は構造体と変わりありません。

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

の手順で行います。

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

#include <stdio.h>

/* 共用体の型枠の宣言 */
union smpl {
	long l;
	int i;
	char c;
};

int main(void)
{
	/* 共用体の宣言と初期化 */
	/* (long型メンバ1 への初期化) */
	union smpl dt = { 0x11111111 }; /* (1) */
	
	/* 共用体の参照 */
	printf("dt.l = 0x%lx\n", dt.l);
	printf("dt.i = 0x%x\n", dt.i);
	printf("dt.c = 0x%x\n", dt.c);
	
	dt.c = 0x22;		/* (2) */
	printf("\ndt.l = 0x%lx\n", dt.l);
	printf("dt.i = 0x%x\n", dt.i);
	printf("dt.c = 0x%x\n", dt.c);
	
	dt.i = 0x3333;		/* (3) */
	printf("\ndt.l = 0x%lx\n", dt.l);
	printf("dt.i = 0x%x\n", dt.i);
	printf("dt.c = 0x%x\n", dt.c);
	
	dt.l = 0x44444444;	/* (4) */
	printf("\ndt.l = 0x%lx\n", dt.l);
	printf("dt.i = 0x%x\n", dt.i);
	printf("dt.c = 0x%x\n", dt.c);
	
	return 0;
} 
※ 配置は処理系によって異なる。
(実行結果)int型2バイトの場合(処理系によって異なる)
dt.l = 0x11111111	---	初期値の0x11111111
dt.i = 0x1111		をlong型、int型、char型
dt.c = 0x11		で参照

dt.l = 0x11111122	---	char型の分のみデータ
dt.i = 0x1122		が代入されている
dt.c = 0x22

dt.l = 0x11113333	---	int型の分のみデータ
dt.i = 0x3333		が代入されている
dt.c = 0x33

dt.l = 0x44444444	---	long型に代入されたデータは
dt.i = 0x4444		int型、char型では、
dt.c = 0x44		その型分のみ表示される

(3)共用体を含む構造体

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

(例)

次のように選択種別によって異なる成績データをもつ学生群 1 と 2 とがある。

これらを構造体の中に共用体を含むことにより、 1つのデータ構造で宣言することができる。

#include <stdio.h>

struct kamoku1 {
	int kokugo;
	int eigo;
	int syakai;
};
struct kamoku2 {
	int suugaku;
	int eigo;
};
struct seiseki {
	int no;
	char name[40];
	int sentaku;
	union kamoku {
		struct kamoku1 k1;
		struct kamoku2 k2;
	}ka;
};

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

16−3.enum

enumは列挙型と呼ばれ、int型整数値の並びに、特定の名前を与えるものです。

例えば、日曜日から土曜日までの曜日に特定の名前を与える場合、通常は「#define」を用いて次のように宣言します。

#define	SUN	0
#define	MON	1
#define	TUE	2
#define	WED	3
#define	THU	4
#define	FRI	5
#define	SAT	6

このマクロ定義を enum を用いると、次のように書き換える事が出来ます。

enum week { SUN, MON, TUE, WED, THU, FRI, SAT };

(1)列挙型の宣言

列挙型の宣言は次のようになります。

enum タグ名 { 名前, 名前, 名前, ・・・};

enum week { SUN, MON, TUE, WED, THU, FRI, SAT }; と宣言する事により、enum は 0 から順に定数に整数値を与えます。

つまり、「SUN=0, MON=1, TUE=2, WED=3, THU=4, FRI=5, SAT=6」の値が与えられます。

もし、値を指定する場合には、

enum week { SUN=10, MON=20, TUE=30, WED=40, THU=50, FRI=60, SAT=70 };

のように「=」で値を指定すれば、希望の値にすることができます。

また、列挙定数は1つ前に指定された値+1を持ちますので、

enum week { SUN=1, MON, TUE, WED, THU, FRI, SAT };

と宣言すると、「SUN=1, MON=2, TUE=3, WED=4, THU=5, FRI=6, SAT=7」の値が与えられます。

(2)列挙型変数の宣言

列挙型を用いる変数の宣言は次のようになります。

enum タグ名 変数名;

先ほど宣言した week で変数 w を定義するには

enum week w;

のようにします。これにより、変数wは常に列挙型を扱う変数として定義され、仮に

w = 5;

などとした場合にはコンパイル時に警告メッセージが出力されます。(警告を発しない処理系もあります。)

#include <stdio.h>

enum week { SUN, MON, TUE, WED, THU, FRI, SAT };	/* ⇒列挙型「week」の宣言 */

int main(void)
{
	enum week w;	/* ⇒列挙型 week で変数 w を宣言 */
	int a;		/* ⇒単に int型で変数 a を宣言 */
	
	w = MON;		/* ⇒変数 w に列挙定数 MON を代入 */
	a = TUE;		/* ⇒変数 a に列挙定数 TUE を代入 */
	
	printf("w = %d\n", w);
	printf("a = %d\n", a);
	
	w = 3;		/* ⇒変数 w に数値定数 3 を代入:コンパイル時警告 */
	a = 4;		/* ⇒変数 a に数値定数 4 を代入 */
	
	printf("w = %d\n", w);
	printf("a = %d\n", a);
	
	return 0;
}
(実行結果)
w = 1
a = 2
w = 3
a = 4
 enum についても有効範囲は考慮してください。
演習
◆◆前ページ  ▲TOP▲  次ページ◆◆

banner
「初心者のためのポイント学習C言語」
Copyright(c) 2000-2004 TOMOJI All Rights Reserved