構造体(Structure)
自分自身の表現
自分自身を表現するには、名前、性別、生年月日、住所、電話番号、などたくさんの情報が必要となる。これらの情報を基に名簿を作ることを考えてみよう。まず、これらの情報をしまう箱の型について考える。名前は文字配列で32文字、住所も文字配列で80文字、電話番号も文字配列で20文字、生年月日はlong int型でよいだろう。性別は男か女のどちらかなので列挙型とする。
char name[32];
long int birth;
char address[80];
char tel[20];
enum {Female,Male} sex;
となる。
この場合、nameやaddressがまとまって配置されているので、これらの事柄は関連している1つの事柄を表していると推測できる。そこで、このような互いに関連しているデータを1つの型として宣言するのに用いるのが構造体(structure)である。
例題9.1 構造体宣言を用いて、上の事柄を宣言してみよう。
解答
struct person { char name[32]; long int birth; char address[80]; char tel[20]; enum {Female,Male} sex; } ; |
ここで、structは構造体宣言を示すキーワードで、次のpersonはこの構造体の名前で、構造タグ名と呼ばれている。{と}に囲まれた中は、構造体のメンバと呼ばれ、メンバどうしの名前が重複しなければ、どのような名前でも使える。
次に、
struct person self_data;
とすると、変数self_dataはperson型と定義される。また、列挙型と同じように
struct person { char name[32]; long int birth; char address[80]; char tel[20]; enum {Female,Male} sex; } self_data; |
とすると、宣言と変数self_dataの定義を同時に行うことができる。
構造体メンバの参照
構造体メンバの参照は
構造体変数名.メンバ名
で行う。例えば、構造体変数名self_dataのメンバbirthの参照は
self_data.birth
と書く。
例題9.2 19530315をキーボードから入力し、self_data.birthに代入する。
解答
struct person{
long int birth;
}self_data;
scanf(“%ld”,&self_data.birth);
printf(“%ld”,self_data.birth);;
とすればよい。
例題9.3 名前、誕生日、住所、電話番号、性別を含んだ個人情報をキーボードから入力し、表示するプログラムを作成しよう。
実行例
解答 列挙型enum {Female,Male}sex;にscanfで読み込んだ値を代入するには、0または1をキーボードから打つ。
出力するには
printf(“%c”,(self_data.sex = = 0)? ‘F’:’M’;)を用いる。これは3項演算子と呼ばれるもので、次のように用いる。
式1?式2:式3
の形式をとり、式1が真ならば、式2の値が式の値となり、偽ならば式3が式の値となる。
#include <stdio.h> #include <string.h> void main(void) { int choice=0; struct person { char name[32]; long int birth; char address[80]; char tel[20]; enum {Female,Male}sex; } self_data; do { choice=1; printf("名前> 誕生日> 住所> 電話番号> 性別> \n"); scanf("%s %ld %s %s %d",&self_data.name, &self_data.birth, &self_data.address,&self_data.tel, &self_data.sex); printf("\n NAME BIRTHDAY ADDRESS TEL SEX\n"); printf("%-10.10s %11.8ld %-30.30s %-10.10s %3c",self_data.name, self_data.birth, self_data.address, self_data.tel, (self_data.sex==0)? 'F':'M'); printf("\n次のデータを入れますか?yesなら0以外"); scanf("%d",&choice); } while (choice != 0); } |
構造体のネスト
struct date birthdate;
を上の構造体の中に入れて新たな構造体を作ることができる。
struct person {
char name[32];
struct date birthdate;
char address[80];
char tel[20];
enum {Female,Male} sex;
}self_data ;
このとき、ネストになっているdate構造体のメンバbirthdateの参照は次のようにして行うことができる。
self_data.date.month
一般的に、
構造体変数名.構造体変数メンバ.構造体変数メンバ….メンバ名
でメンバの参照ができる。
共用体(union)
共用体は、構文や参照方法は構造体と同じであるが、構造体と違って各メンバが同じメモリを使用する。つまり、1つの箱を複数の用途に共有出来るようになっている。先ほどのperson型の構造体を共用体で定義してみよう。
union person{
char name[32];
struct date birthdate;
char address[80];
char tel[20];
enum {Female,Male} sex;
}self_data ;
最初のunionは、共用体を示すキーワードで、次のpersonはこの共用体の名前で、共用体タグと呼ばれている。{と}に囲まれた中は、共用体のメンバと呼ばれ、メンバどうしの名前が重複しなければ、どのような名前でも使える。
構造体へのポインタ
struct tag{
int x;
double y;
} test;
この構造体へのポインタは次のように定義する。
struct tag *p;
p = &test;
ここで、構造体メンバxのアドレスはどのように求めるのだろうか。
&test.xと書けば、xのアドレスが求まる。
また、構造体へのポインタpを利用して、構造体の各メンバを間接参照することも出来る。
間接参照「*」を思い出すと、p = &testより*pは構造体変数testを指す。よって各メンバは(*p).x,(*p).yで参照できる。ここで、括弧は必要である。なぜなら、*と.の優先順位は.の方が上である。
しかし、このように書くよりもCでは構造体をポインタで間接参照できるように「→」というアロー演算子(structure operator)が用意されている。この演算子を用いると、(*p).xはp→xと記述でき、視覚的にも分かりやすい。ただし、→はマイナス記号と不等号「>」を用いて表す。
例題9.4 構造体のメンバをポインタを用いて参照する。
実行例
#include <stdio.h> void main(void) { struct tag{ int x; double y; } test; struct tag *p; p = &test; p ->x = 30; p ->y = 15.3; printf("x = %d y = %lf\n",p -> x, p->y); } |
typedefと構造体
typedefは既存のデータ型に,新しい名前をつける予約語である。新しいデータ型の名前をtypedefを用いて次のように記述する。
typedef データ型 新しい名前;
typedefを用いて定義された新しい名前は既存のデータ型と同じように変数の宣言に使うことができる。
構造体のように,型の宣言の記述が長くなるようなとき,typedef指定子を用いることが多い。
日付と時間
日付や時間を知るには<time.h>で宣言されているtime関数とlocaltime関数を用いる。
time関数は、現在のカレンダ時間を返す関数で型time_tはlong型を<time.h>でtypedef宣言している。
localtime関数は構造体struct tmのポインタ型を返す関数であり、構造体変数tmのメンバは次のようになっている。
int tm_sec; /*second after the minute – [0,59] */ int tm_min; /*minute after the hour – [0,59] */ int tm_hour; /*hours after the midnight – [0,23] */ int tm_mday; /*day of the month – [1,31] */ int tm_mon; /*months since January – [0,11] */ int tm_year; /*years since 1900 */ int tm_wday; /*days since Sunday – [0,6] */ int tm_yday; /*days since January 1 – [0,365] */ int tm_isdst; /*daylight savings time flag */ |
例題9.5 プログラムを実行した時間が何時かを返すプログラムを作成しよう。
実行例
解答 日付や時間を知るには、まずtime関数でカレンダ時間を求め、この時間をlocaltime関数で読み込み構造体変数tpに代入する。この後、時間が知りたければtp->tm_hourを表示すればよい。
#include <stdio.h> #include <time.h> void main(void) { struct tm *tp; time_t t; t = time(&t); // カレンダ時間をtに代入 tp = localtime(&t); //カレンダ時間をローカル時間に変更 printf("現在の時刻は%dです\n",tp->tm_hour); // tp->tm_hourで時間を構造体のメンバを参照 } |
例題9.6 何月何日何時何分何秒を表示するプログラムを作成しよう。
実行例
解答
#include <stdio.h> #include <time.h> void main(void) { struct tm *tp; time_t t; t = time(&t); tp = localtime(&t); printf("%d月%d日%d時%d分%d秒\n",tp->tm_mon+1,tp->tm_mday,tp->tm_hour,tp->tm_min,tp->tm_sec); } |
例題9.7 プログラムを実行した時間が午前か午後かを判断して、午前なら午前です、午後なら午後ですと返すプログラムを作成しよう。
考え方 まず、午前か午後かを判断する関数を作る。この関数をapre_midiという名前にする。
apre_midiは次のようになる。
int apre_mid(void) { struct tm *tp; time_t t; if( (t = time(&t)) == -1) { perror(“time function error \n”); exit(1); } tp = localtime(&t); return(tp -> tm_hour < 12); } |
解答
#include <stdio.h> #include <time.h> #include <stdlib.h> int apre_mid(void); void main(void) { if(apre_mid()) { printf("午前です\n"); } else { printf("午後です\n"); } } int apre_mid(void) { struct tm *tp; time_t t; if( (t = time(&t)) == -1) { perror("time function error \n"); exit(1); } tp = localtime(&t); return(tp -> tm_hour < 12); } |