異なる型の変数をまとめる
まとめてデータを扱いたい場合
第13章では、同じ型の複数の変数をまとめて扱う配列を説明しました。
しかし、場合によっては、異なる型もまとめて扱いたい場合があります。
また、各要素を番号で区別するよりも、名前で区別した方が便利なこともあります。
たとえば、学校で生徒の身体測定の結果を保存しておきたい場合には、
各生徒につき、クラス、学年、出席番号、名前、身長、体重、などのデータが必要です。
これを、素直に変数で用意した場合、次のようになります。
身長と体重は、小数第1位まで表現することが多いので、実数型にしています。
当然、これでも、生徒のデータとして十分機能します。
しかし、これらのデータは、すべて関連したデータであるにもかかわらず、
1つ1つが別の変数として宣言されているため、あまりわかりやすくありません。
この様な場合に、複数の異なる型の変数を1つにまとめて取り扱う方法として、
構造体という機能が用意されています。
複数の異なる型をまとめて作られた型のこと。
構造体では、複数の型をまとめた新しい型を作り出すことができます。
たとえば、生徒のデータを新しい型の構造体として作るには、次のようにします。
構造体の型を宣言する時には、始めにstruct(ストラクト)をつけます。
その次には、変数と同じルールの元、新しく宣言する構造体の型名をつけます。
この型名をとくに、構造体タグ名と呼ぶことがあります。
作成した構造体それ自体の名前。
型名ではないため、扱い方はすこし異なります。
今回は、生徒と言う意味の、studentという名前をつけています。
後は、{}で囲んだ中に、まとめたい変数の型と名前を宣言します。
この様にして、新しい構造体の型を作ることができますが、
これだけでは、単に型を宣言しただけなので、実際に使うことはできません。
実際に使うためには、構造体の型の変数を宣言する必要があります。
構造体の型の変数を宣言するには、次のようにします。
この例では、student構造体タグの、data構造体変数を宣言しています。
構造体タグの変数を宣言する場合、始めにstructをつけます。
次に、構造体の型名、最後に、構造体変数の名前を指定します。
今後、構造体の型を構造体タグ、構造体タグで宣言された変数を構造体変数と呼びます。
この様にして、構造体タグと構造体変数を宣言できます。
次のプログラムは、構造体タグと構造体変数を宣言する例です。
この例のように、構造体タグは、関数よりも先に宣言するのが普通です。
なぜなら、そうすれば、後に登場するすべての関数でこの構造体が使えるからです。
しかし、場合によっては、異なる型もまとめて扱いたい場合があります。
また、各要素を番号で区別するよりも、名前で区別した方が便利なこともあります。
たとえば、学校で生徒の身体測定の結果を保存しておきたい場合には、
各生徒につき、クラス、学年、出席番号、名前、身長、体重、などのデータが必要です。
これを、素直に変数で用意した場合、次のようになります。
ソースコード
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
clas?
clas は class が正しいスペルです。
ですが、classという名前は使用できないため、あえて clas にしています。
なぜなら、ほとんどのC言語コンパイラはC++にも対応していますが、
C++には クラスという機能があり、class はその機能のために使用されています。
つまり、C++では class は予約語となっているため、名前には使用できないのです。
clas が気に入らないようでしたら group にすることもできます。
ですが、classという名前は使用できないため、あえて clas にしています。
なぜなら、ほとんどのC言語コンパイラはC++にも対応していますが、
C++には クラスという機能があり、class はその機能のために使用されています。
つまり、C++では class は予約語となっているため、名前には使用できないのです。
clas が気に入らないようでしたら group にすることもできます。
身長と体重は、小数第1位まで表現することが多いので、実数型にしています。
当然、これでも、生徒のデータとして十分機能します。
しかし、これらのデータは、すべて関連したデータであるにもかかわらず、
1つ1つが別の変数として宣言されているため、あまりわかりやすくありません。
この様な場合に、複数の異なる型の変数を1つにまとめて取り扱う方法として、
構造体という機能が用意されています。
キーワード
【構造体】
複数の異なる型をまとめて作られた型のこと。
構造体では、複数の型をまとめた新しい型を作り出すことができます。
たとえば、生徒のデータを新しい型の構造体として作るには、次のようにします。
構造体
struct student
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
};
構造体の型を宣言する時には、始めにstruct(ストラクト)をつけます。
その次には、変数と同じルールの元、新しく宣言する構造体の型名をつけます。
この型名をとくに、構造体タグ名と呼ぶことがあります。
キーワード
【構造体タグ名】
作成した構造体それ自体の名前。
型名ではないため、扱い方はすこし異なります。
今回は、生徒と言う意味の、studentという名前をつけています。
後は、{}で囲んだ中に、まとめたい変数の型と名前を宣言します。
この様にして、新しい構造体の型を作ることができますが、
これだけでは、単に型を宣言しただけなので、実際に使うことはできません。
実際に使うためには、構造体の型の変数を宣言する必要があります。
構造体の型の変数を宣言するには、次のようにします。
構造体の型の変数を宣言
struct student data;
この例では、student構造体タグの、data構造体変数を宣言しています。
構造体タグの変数を宣言する場合、始めにstructをつけます。
次に、構造体の型名、最後に、構造体変数の名前を指定します。
今後、構造体の型を構造体タグ、構造体タグで宣言された変数を構造体変数と呼びます。
C++では
C言語の拡張版であるC++では
structをつけなくても構造体変数を宣言できます。
現在のコンパイラはほとんどがC++用なので、
structをつけなくても動作してしまうことがおおいです。
structをつけなくても構造体変数を宣言できます。
現在のコンパイラはほとんどがC++用なので、
structをつけなくても動作してしまうことがおおいです。
この様にして、構造体タグと構造体変数を宣言できます。
次のプログラムは、構造体タグと構造体変数を宣言する例です。
構造体タグと構造体変数を宣言
struct student
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
};
int main(void)
{
struct student data;
return 0;
}
この例のように、構造体タグは、関数よりも先に宣言するのが普通です。
なぜなら、そうすれば、後に登場するすべての関数でこの構造体が使えるからです。
構造体の使い方
前項では、構造体タグと構造体変数の宣言について説明しました。
せっかく宣言した構造体なのですから、さっそく使ってみることにしましょう!
構造体変数は、元となった構造体タグで宣言されていたすべての型を持っています。
配列の時には、同じ型の変数を番号によって区別していましたが、
構造体変数では、型に関係なく、すべての要素を名前によって区別します。
構造体変数の持つ1つの要素にアクセスするには、次のようにします。
ここで、. は小数点記号を使用します。カンマではありません。
ただし、小数点記号を流用しているだけであって、少数そのものとはまったく関係ありません。
ポインタといい、C言語って無関係の記号を流用する悪い癖があるよね・・・
この様にして、個々の要素にアクセスできます。
次のプログラムは、先ほどの構造体の、year要素を使う例です。
このプログラムの実行結果は次の通りになります。
この例では、student構造体タグのdata構造体変数のyear要素を使っています。
この様にしてアクセスした場合、もはや普通の変数とまったく同じです。
また、同様にして、配列にアクセスすることもできます。
次のプログラムは、先ほどの構造体の、name要素にアクセスする例です。
このプログラムの実行結果は次の通りになります。
もちろん、[]をつけて、配列の各要素にアクセスすることも可能です。
せっかく宣言した構造体なのですから、さっそく使ってみることにしましょう!
構造体変数は、元となった構造体タグで宣言されていたすべての型を持っています。
配列の時には、同じ型の変数を番号によって区別していましたが、
構造体変数では、型に関係なく、すべての要素を名前によって区別します。
構造体変数の持つ1つの要素にアクセスするには、次のようにします。
構造体変数の持つ1つの要素にアクセス
構造体変数名.要素名
ここで、. は小数点記号を使用します。カンマではありません。
ただし、小数点記号を流用しているだけであって、少数そのものとはまったく関係ありません。
ポインタといい、C言語って無関係の記号を流用する悪い癖があるよね・・・
この様にして、個々の要素にアクセスできます。
次のプログラムは、先ほどの構造体の、year要素を使う例です。
ソースコード
#include <stdio.h>
struct student
{
int year; /* 学年 */
int clas; /* クラス */
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;
}
このプログラムの実行結果は次の通りになります。
実行結果
10
この例では、student構造体タグのdata構造体変数のyear要素を使っています。
この様にしてアクセスした場合、もはや普通の変数とまったく同じです。
また、同様にして、配列にアクセスすることもできます。
次のプログラムは、先ほどの構造体の、name要素にアクセスする例です。
ソースコード
#include <stdio.h>
#include <string.h>
struct student
{
int year; /* 学年 */
int clas; /* クラス */
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
もちろん、[]をつけて、配列の各要素にアクセスすることも可能です。
構造体変数自体の処理
前項では、構造体の各要素にアクセスする方法を説明しましたが、
これでは、見た目には確かにまとまっているようにも見えるものの、
実際の使い方は、普通の変数とまったく同じで、あまり意味がないようにも思えます。
しかし、構造体の場合、構造体変数自体を変数として取り扱うことができます。
たとえば、構造体変数同士で、代入を行うことが可能となっています。
次のプログラムは、構造体変数同士で代入を行う例です。
このプログラムの実行結果は次の通りになります。
このプログラムでは、まず、data1の各要素に代入を行っています。
そして、data2にdata1を代入しています。
その結果を表示してみると、data1とdata2の中身は同じになっています。
この様に、構造体変数では、全要素を一括して代入できます。
あとで説明することですが、他にも、関数の引数として利用したりなど、
構造体変数はそれ自体を1つの変数として使うことが出来、
1つ1つ代入しなければならなかった配列よりも便利です。
今までは、構造体タグを宣言してから構造体を使用していました。
この場合、構造体を使う時は、必ずstructが必要になります。
しかし、構造体タグを、新しい型として1度に宣言してしまう方法があります。
C言語では、新しい型を宣言するtypedef(タイプデフ)が用意されています。
詳しい説明はあとでしますが、次のようにtypedefを使うと新しい型を宣言できます。
これを利用すると、構造体タグを直接新しい型にできます。
次のプログラムは、構造体タグを元に新しい型を作る例です。
この例では、student_tagタグを、student型にできます。
こうすれば、構造体変数を宣言する時に、structが不要になります。
今後、先ほどの方法で宣言された型を、構造体型と呼ぶことにします。
構造体型にすることで、型と同様に扱えるため、便利なのですが、
毎回 typedef を使って型を定義するのは面倒です。
より簡単にするために、構造体タグと構造体型を同時に宣言してしまうことができます。
さらに、この場合には、構造体タグを省略できます。
この方法が、1番簡潔に構造体型を宣言できます。
現在では上記の形がほぼ慣用句となっておりますので、この形で覚えてしまっていいと思います。
これでは、見た目には確かにまとまっているようにも見えるものの、
実際の使い方は、普通の変数とまったく同じで、あまり意味がないようにも思えます。
しかし、構造体の場合、構造体変数自体を変数として取り扱うことができます。
たとえば、構造体変数同士で、代入を行うことが可能となっています。
次のプログラムは、構造体変数同士で代入を行う例です。
ソースコード
#include <stdio.h>
#include <string.h>
struct student
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
};
int main(void)
{
struct student data1, data2;
/* data1 へ代入 */
data1.year = 3;
data1.clas = 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.clas = %d : data2.clas = %d\n", data1.clas, data2.clas);
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.year = 3 : data2.year = 3
data1.clas = 4 : data2.clas = 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
data1.clas = 4 : data2.clas = 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
このプログラムでは、まず、data1の各要素に代入を行っています。
そして、data2にdata1を代入しています。
その結果を表示してみると、data1とdata2の中身は同じになっています。
この様に、構造体変数では、全要素を一括して代入できます。
あとで説明することですが、他にも、関数の引数として利用したりなど、
構造体変数はそれ自体を1つの変数として使うことが出来、
1つ1つ代入しなければならなかった配列よりも便利です。
構造体変数の比較
構造体変数は、それ自体を1つの変数として使えると説明しましたが、
残念ながら、構造体変数同士での演算や比較は行えません。つまり、
struct student data1,data2;
/* data1とdata2に代入 */
if (data1 == data2) {
/* 何かの処理 */
}
のようなプログラムは書けません。
加算や減算はともかく、同一比較はできてもいいと思うんだけどなあ・・・
残念ながら、構造体変数同士での演算や比較は行えません。つまり、
struct student data1,data2;
/* data1とdata2に代入 */
if (data1 == data2) {
/* 何かの処理 */
}
のようなプログラムは書けません。
加算や減算はともかく、同一比較はできてもいいと思うんだけどなあ・・・
今までは、構造体タグを宣言してから構造体を使用していました。
この場合、構造体を使う時は、必ずstructが必要になります。
しかし、構造体タグを、新しい型として1度に宣言してしまう方法があります。
C言語では、新しい型を宣言するtypedef(タイプデフ)が用意されています。
詳しい説明はあとでしますが、次のようにtypedefを使うと新しい型を宣言できます。
ソースコード
typedef 新しい型の形 新しい型名
これを利用すると、構造体タグを直接新しい型にできます。
次のプログラムは、構造体タグを元に新しい型を作る例です。
ソースコード
struct student_tag
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
};
typedef struct student_tag student;
この例では、student_tagタグを、student型にできます。
こうすれば、構造体変数を宣言する時に、structが不要になります。
今後、先ほどの方法で宣言された型を、構造体型と呼ぶことにします。
構造体型にすることで、型と同様に扱えるため、便利なのですが、
毎回 typedef を使って型を定義するのは面倒です。
より簡単にするために、構造体タグと構造体型を同時に宣言してしまうことができます。
ソースコード
typedef struct student_tag
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
} student;
さらに、この場合には、構造体タグを省略できます。
ソースコード
typedef struct
{
int year; /* 学年 */
int clas; /* クラス */
int number; /* 出席番号 */
char name[64]; /* 名前 */
double stature; /* 身長 */
double weight; /* 体重 */
} student;
この方法が、1番簡潔に構造体型を宣言できます。
現在では上記の形がほぼ慣用句となっておりますので、この形で覚えてしまっていいと思います。
本サイトについて
苦しんで覚えるC言語(苦C)はC言語入門サイトの決定版です。
C言語の基本機能を体系立てて解説しており、
市販書籍と同等以上の完成度です。




