これまで、1つの変数に1つの値しか入れられませんでした。
たとえば、int型の変数には整数を、char型の変数には文字を入れます。
しかし、プログラムの中では「いくつかの情報をひとまとめにして扱いたい」ことがあります。
たとえば、「生徒の情報」なら次のようなデータをまとめたいですよね。
- 名前(文字列)
- 年齢(整数)
- 成績(小数)
このように、異なる種類のデータを1つにまとめる仕組みを「構造体(struct)」といいます。
また、同じ場所を複数の変数で共有して使う仕組みを「共用体(union)」といいます。
今回は、この2つの仕組みについて、初めての人でもわかるように解説します。
構造体とは
構造体は、異なる種類のデータをまとめて1つのかたまりにしたいときに使います。
たとえば、生徒の情報(名前・年齢・成績)をまとめて扱いたいときに便利です。
構造体の定義の構文は次の通りです。
struct 構造体型名 {
型名1 メンバー名1;
型名2 メンバー名2;
型名3 メンバー名3;
};構造体はブロックの中に変数などをまとめて記述したものです。
例えば、名前・年齢・成績の生徒の情報を管理するための構造体型 struct Student の定義は、次のようになります。
struct Student {
char name[20]; // 名前
int age; // 年齢
float score; // 成績
};ここでは、構造体に、char型の配列のメンバーname[20]と、int型の変数 age と、float型の変数 score を定義しています。
構造体型のブロック内に定義した変数名は、構造体のメンバーと呼びます。
あくまで「構造体型の中にどんな項目を持つか」を決めているだけで、まだ、変数そのものではないからです。
構造体変数を定義する
構造体型を定義したら、次は、その構造体型を使って、変数を定義します。
構造体変数の定義の構文は次の通りです。
構造体型名 構造体変数名;先ほど定義した構造体型を、変数 student1 として定義した場合は、次の通りです。
struct Student student1;定義したときに、student1 の中にあるname、age、score のためのメモリが実際に確保され、使えるようになります。
構造体変数の使い方
構造体変数を定義したら、その中のメンバーにアクセスします。
メンバーに値を入れたり取り出したりするときは、ドット( . )を使うというルールがあります。
student1.age = 15; // 年齢に代入
student1.score = 92.5; // 成績に代入
printf("%d\n", student1.age); // 年齢を表示このように、ドット( . )を使うことで代入や参照することができます。
次に実際にプログラミングしてみましょう。
#include <stdio.h>
struct Student {
char name[20];
int age;
float score;
};
int main(void)
{
struct Student student1;
// 値を代入
student1.age = 15;
student1.score = 92.5;
printf("年齢:%d\n", student1.age);
printf("成績:%.1f\n", student1.score);
return 0;
}※文字列(配列)のメンバーには、= で代入することはできません。
このため、別の方法で代入する必要がありますが、そのあたりは、まだ解説できていないので、別記事で紹介します。
構造体の初期化
変数は、宣言と同時に初期化することができました。
構造体も同様に宣言と同時に初期化することができます。
構造体の初期化の構文は次の通りです。
構造体型名 構造体変数名 = { 初期値1, 初期値2, ... };先ほどの、構造体型 struct Student を初期化する場合は、次のようになります。
struct Student student1 = { "Yamada", 15, 92.5 };この場合は、
name:"Yamada"
age:15
score:92.5
が順にセットされます。
※構造体型の定義で記述されているメンバーの順に初期値を並べる必要があります。
一部だけ初期化する場合
次のように、一部だけ初期化することもできます。
初期値を省略したメンバーには、0が代入されます。
struct Student student1 = { "Yamada" };この場合は、
name:"Yamada"
age:0
score:0.0
になります。
次のように、途中の初期値だけを入れるようなことはできません。
struct Student student1 = { , 15, }; // × ageだけ初期値を入れることはできない
struct Student student1 = { , , 92.5 }; // × scoreだけ初期値をいれることはできないC99仕様では、メンバー名を指定して初期化する書き方もあるため、初期化したいメンバーだけ初期値を書くことができますが、このやり方については、中級編、または、上級編で紹介します。
構造体同士の代入
特にIoTでは、実務で使うことがあまりないのですが、構造体同士の代入もできます。
構造体同士を次のように 1行でコピー できます。
struct Student a = { "Yamada", 15, 92.5 };
struct Student b;
b = a; // a の名前・年齢・成績が b に丸ごとコピーされるこのように、構造体変数同士を代入するだけで、全てのメンバーの値がコピーされます。
typedefで名前を割り当てる
構造体型は、struct Student という長い型名となっています。
構造体変数を定義する際、この長い型名を使って定義しなければならないのは不便です。
そこで、構造体型の型名を短縮するために、typedef というキーワードを使う方法が使われています。typedef の構文は次の通りです。
typedef 元の型 新しい型名;実際に typedef を使って構造体型の型名を短縮してみましょう。
typedef struct Student {
char name[20];
int age;
float score;
} Student;今までの構造体型の定義の最初に、typedef と付けます。
その後に、struct { … } が「元の型」になります。
構造体型の定義の終わりのブロックの後の Student が新しい型名になります。
このように構造体型を定義すると、次のように新しい型名で構造体型の変数が定義できます。
Student s1;この Student は、構造体の別名として機能しています。struct 〜 を使わなくても変数を作れるようになるため、コードが読みやすく、短くなるメリットがあります。
一般的には、typedef を付けて別名を定義しています。
まとめ
- 構造体は、異なる種類のデータをひとまとめにして扱うための仕組み
{}の中に書くname、age、scoreなどは、構造体の「メンバー」と呼ばれる- 構造体型を定義しただけではメモリは作られず、構造体変数を作ったときに実体ができる
- メンバーにアクセスするときは ドット(.) を使う
例:student1.age - 構造体は { } を使って初期化できる
- 初期値はメンバーの順番どおりに書く
- 一部だけ初期化すると、残りは 0 で初期化される
{}を使う初期化では、途中のメンバーだけ初期化することはできない- C99 以降は メンバー名を指定して初期化することもできる
- 構造体同士は 1行で丸ごと代入できる(
s2 = s1;) - typedef で別名を定義することで、structを付けなくても構造体型の変数を定義することができる