第15章で構造体について説明しましたので、C言語初心者の方が学習すべき「データ型」についてはこれ以上は必要ないような気もします。 ただ、一部の入門書には「typedef」、「union」、「enum」についても説明がありますので、この章で取り上げたいと思います。
「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; }
「#define」とよく似た機能であるが、#define はプリプロセッサにより解釈され、typedef はコンパイラにより解釈される。
また、単に同義語を作る#defineに比べ、typedef は「型」の同義語を作るという目的が明確である。
union(共用体)は、同一のデータ領域を複数個の異なるデータ型が共用するようにしたものです。
共用体は、宣言の「struct」が「union」になるだけで、それ以外は宣言の仕方や使い方などは構造体と全く同じです。 しかし、共用体は各メンバがすべて同じアドレスから割り振られている点が構造体とは異なります。
たとえば、smpl という共用体タグ名で次のような共用体を宣言すると、メモリ上は図のようになります。
共用体の使用手順は構造体と変わりありません。
の手順で行います。
ただし、初期化には注意が必要です。 共用体の初期化はその最初のメンバの値で行わなければなりません。
#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; } |
※ 配置は処理系によって異なる。 |
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 その型分のみ表示される
ここにある構造を持つデータがあるとして、ある条件のときのみデータの一部を変更したいとします。 単に構造体のみでデータを扱う場合には、条件によって 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
※ 構造体の中に共用体を含む場合、参照の際には面倒でも 全てのタグとメンバをつなげて記述しなければなりません。
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 };
列挙型の宣言は次のようになります。
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」の値が与えられます。
列挙型を用いる変数の宣言は次のようになります。
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 についても有効範囲は考慮してください。
「初心者のためのポイント学習C言語」 Copyright(c) 2000-2004 TOMOJI All Rights Reserved