第13章では、同じ型の複数の変数をまとめて扱う配列を説明しました。
しかし、場合によっては、異なる型もまとめて扱いたい場合があります。
また、各要素を番号で区別するよりも、名前で区別した方が便利なこともあります。
例えば、学校で生徒の身体測定の結果を保存しておきたい場合には、
各生徒につき、クラス、学年、出席番号、名前、身長、体重、などのデータが必要です。
これを、素直に変数で用意した場合、次のようになります。
身長と体重は、小数第1位まで表現することが多いので、実数型にしています。int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */
当然、これでも、生徒のデータとして十分機能します。
しかし、これらのデータは、全て関連したデータであるにもかかわらず、
1つ1つが別の変数として宣言されているため、あまりわかりやすくありません。
この様な場合に、複数の異なる型の変数を1つにまとめて取り扱う方法として、
構造体という機能が用意されています。
構造体では、複数の型をまとめた新しい型を作り出すことが出来ます。
[ 構造体 ]
複数の異なる型をまとめて作られた型のこと。
構造体の型を宣言する時には、始めにstruct(ストラクト)をつけます。struct student { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ };
今回は、生徒と言う意味の、studentという名前をつけています。
[ 構造体タグ名 ]
作成した構造体それ自体の名前。
厳密には型名ではないので注意するように。
この様にして、新しい構造体の型を作ることが出来ますが、
これだけでは、単に型を宣言しただけなので、実際に使うことは出来ません。
実際に使うためには、構造体の型の変数を宣言する必要があります。
構造体の型の変数を宣言するには、次のようにします。
この例では、student構造体タグの、data構造体変数を宣言しています。struct student data;
この様にして、構造体タグと構造体変数を宣言することが出来ます。
[ C++では ]
C言語の拡張版であるC++では
structをつけなくても構造体変数を宣言出来ます。
現在のコンパイラはほとんどがC++用なので、
structをつけなくても宣言出来てしまいます。
この例のように、構造体タグは、関数よりも先に宣言するのが普通です。struct student { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ }; int main(void) { struct student data; return 0; }
前項では、構造体タグと構造体変数の宣言について説明しましたが、
いくら宣言しても、実際に使ってみないことには意味がありません。
構造体変数は、元となった構造体タグで宣言されていた全ての型を持っています。
配列の時には、同じ型の変数を番号によって区別していましたが、
構造体変数では、型に関係なく、全ての要素を名前によって区別します。
構造体変数の持つ1つの要素にアクセスするには、次のようにします。
ここで、. とは、小数点記号のことです。カンマではありません。構造体変数名.要素名
このプログラムの実行結果は次の通りになります。#include <stdio.h> struct student { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ }; int main(void) { struct student data; data.year = 10; /* year要素にアクセス */ printf("%d\n",data.year); return 0; }
この例では、student構造体タグのdata構造体変数のyear要素を使っています。
10
このプログラムの実行結果は次の通りになります。#include <stdio.h> #include <string.h> struct student { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ }; int main(void) { struct student data; strcpy(data.name,"MARIO"); printf("%s\n",data.name); return 0; }
もちろん、[]をつけて、配列の各要素にアクセスすることも可能です。
MARIO
前項では、構造体の各要素にアクセスする方法を説明しましたが、
これでは、見た目には確かにまとまっているようにも見えるものの、
実際の使い方は、普通の変数と全く同じで、あまり意味がないようにも思えます。
しかし、構造体の場合、構造体変数自体を変数として取り扱うことが出来ます。
例えば、構造体変数同士で、代入を行うことが可能となっています。
次のプログラムは、構造体変数同士で代入を行う例です。
このプログラムの実行結果は次の通りになります。#include <stdio.h> #include <string.h> struct student { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ }; int main(void) { struct student data1,data2; /* data1 へ代入 */ data1.year = 3; data1.class = 4; data1.number = 18; strcpy(data1.name,"MARIO"); data1.stature = 168.2; data1.weight = 72.4; data2 = data1; /* data1の内容をdata2へコピー */ /* data1とdata2の内容を表示 */ printf("data1.year = %d : data2.year = %d\n",data1.year,data2.year); printf("data1.class = %d : data2.class = %d\n",data1.class,data2.class); printf("data1.number = %d : data2.number = %d\n",data1.number,data2.number); printf("data1.name = %s : data2.name = %s\n",data1.name,data2.name); printf("data1.stature = %f : data2.stature = %f\n",data1.stature,data2.stature); printf("data1.weight = %f : data2.weight = %f\n",data1.weight,data2.weight); return 0; }
このプログラムでは、まず、data1の各要素に代入を行っています。
data1.year = 3 : data2.year = 3
data1.class = 4 : data2.class = 4
data1.number = 18 : data2.number = 18
data1.name = MARIO : data2.year = MARIO
data1.stature = 168.200000 : data2.stature = 168.200000
data1.weight = 72.400000 : data2.weight = 72.400000
この様に、構造体変数では、全要素を一括して代入することが出来ます。
後で説明することですが、他にも、関数の引数として利用したりなど、
構造体変数はそれ自体を1つの変数として使うことが出来、
1つ1つ代入しなければならなかった配列よりも便利です。
今までは、構造体タグを宣言してから構造体を使用していました。
[ 構造体変数の比較 ]
構造体変数は、それ自体を1つの変数として使えると説明しましたが、
残念ながら、構造体変数同士での演算や比較は行えません。つまり、
のようなプログラムは書けません。struct student data1,data2; /* data1とdata2に代入 */ if (data1 == data2) { /* 何かの処理 */ }
C言語では、新しい型を宣言するtypedef(タイプデフ)が用意されています。
詳しい説明は後でしますが、次のようにtypedefを使うと新しい型を宣言出来ます。
これを利用すると、構造体タグを直接新しい型に出来ます。typedef 新しい型の形 新しい型名
この例では、student_tagタグを、student型にすることが出来ます。struct student_tag { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ }; typedef struct student_tag student;
しかし、その為にtypedefを使って型を定義するのは面倒です。
その場合、構造体タグと構造体型を1度に宣言してしまうことが出来ます。
更に、この場合、新しい型を定義出来れば、構造体タグを省略できます。typedef struct student_tag { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ } student;
この方法が、1番簡潔に構造体型を宣言することが出来ます。typedef struct { int year; /* 学年 */ int class; /* クラス */ int number; /* 出席番号 */ char name[64]; /* 名前 */ double stature; /* 身長 */ double weight; /* 体重 */ } student;