今回は、構造体の基本的な使い方を解説していきます。
新人に教えるつもりで書いているので、図やサンプルコードが多めになっています。もし構造体について理解が深まっていない人は、今回でマスターしていきましょう!
構造体の基本的な考え方
構造体は、「各変数をギュッと1つにまとめた箱のようなもの」だと、私は思っています。
例えば、あるユーザーの情報を新規で登録する時、”名前”、”ID名”、”パスワード”、”メールアドレス”など、様々な情報が必要になります。
それらの情報に対して、変数を1つ1つ定義してもプログラムは出来ますが、またユーザーが増えたら同じように”名前”、”ID名”、”パスワード”、”メールアドレス”が必要になりますよね。
これだとユーザーが増えるたびに変数の数が4つずつ増えていき、変数の管理が難しくなります。
これを「構造体を作成して1つに箱にまとめてしまう」というのが、構造体の考え方になります。
構造体さえ作ってしまえば、他のユーザーの情報を登録した場合でも、すごく分かりやすく管理することができます。
画像の通り、構造体を作成した方が、スッキリしてまとまっていると思います。
構造体の宣言と定義の方法
実際に構造体の作り方をソースコードを書きながら説明していきます。
1. 構造体のひな型を作る(構造体の宣言)
まずは、構造体のひな型を作っていきます。もし今からクッキーを作るとしたら、クッキーの型を作っていくイメージです。
構造体を作るには、“struct”という修飾子を付ける必要があります。”struct”があることによって、「これは構造体です!」という宣言をすることができます。
後は、struct修飾子の後に構造体名を添えて、構造体の中に入れ込みたい変数(メンバ変数と言う)を書いてあげれば完成です。
2. 構造体の定義方法
構造体の宣言をしてひな型を作った後は、使用したい箇所で構造体の定義をします。先ほどのクッキーの例で言うと、実際にクッキーを作っていくイメージです。
構造体の型は、「struct 構造体名」で1セットなので、実際に構造体の実体を定義するときも「struct 構造体名 構造体の実体名」というように書きます。
【補足】
ちなみに、構造体の宣言は基本的に関数の外に書いて、グローバル扱いにすることが多いです。
typedefで構造体を定義しておくと定義が楽になる
構造体の定義は、「struct 構造体名 構造体の実体名」と書くのですが、毎回structと書くのが少し面倒です。
そんなときは、typedefを使って構造体を宣言しておくと、楽に構造体を定義が出来ます。と言うか、実際にはtypedef側で書くことがほとんどです。
typedefを使って構造体を宣言すると、以下のように宣言・定義することができます。
typedefの構造体の宣言時は、構造体名を省略することが出来るので、「typedef struct」だけ書いてもOKです。(どちらかと言うと、省略することが多いと思います)
構造体のメンバ変数の使い方
構造体のメンバ変数は、構造体の実体名を定義した後、“.”(ドット)を使って参照したり値を代入します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* User型の構造体宣言 */ typedef struct User { char *name ; //ユーザー名 int id ; //ID char *pwd ; //パスワード char *mailAddress ; //メールアドレス }st_User; int main() { st_User Tanaka; //構造体の定義 st_User Suzuki; //構造体の定義 /* 1人目のユーザー初期化 */ Tanaka.name = "田中" ; Tanaka.id = 1 ; Tanaka.pwd = "XXX" ; Tanaka.mailAddress = "XXXX@XXX.com" ; /* 2人目のユーザー初期化 */ Suzuki.name = "鈴木" ; Suzuki.id = Tanaka.id + 1 ; Suzuki.pwd = "YYY" ; Suzuki.mailAddress = "YYYY@YYY.com" ; return 0; } |
構造体のアドレス渡しと使い方
構造体では、構造体の先頭アドレス(ポインタ)を関数の引数にして、構造体ごと関数に渡すことがあります。
構造体ごと関数の引数にしてしまえば、一つ一つ変数を関数の引数にしなくて済むため、スッキリしたコードになります。
構造体がポインタの場合の変数の参照方法
また、構造体の実体がポインタの場合は、”.”(ドット)ではなく、”->”(アロー演算子)を使います。
「どんなときにアロー演算子を使うんだっけ。。?」
忘れていくうちに、こんな悩みが出てくる新人が多かったので、アロー演算子についてもう少し説明をします。
変数の場合、ポインタの値を参照するには、”*”(アスタリスク)を使って参照する必要がありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include<stdio.h> void func(int *ptr); int main() { int aaa; func(&aaa); printf("aaa...%d\n",aaa); return 0 ; } void func(int *ptr) { *ptr = 10; } |
1 |
aaa...10 |
実は構造体がポインタの場合も同様に、”*”(アスタリスク)を使って参照することが出来ます。それが以下のコードになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include<stdio.h> /* User型の構造体宣言 */ typedef struct User { char *name ; //ユーザー名 int id ; //ID char *pwd ; //パスワード char *mailAddress ; //メールアドレス }st_User; void func1(st_User *p_user); void func2(st_User *p_user); int main() { st_User Tanaka; //構造体の定義 st_User Suzuki; //構造体の定義 /* 1人目のユーザー初期化 */ func1(&Tanaka); /* 2人目のユーザー初期化 */ func2(&Suzuki); return 0; } void func1(st_User *p_user) { /* ユーザー名を登録 */ p_user -> name = "田中"; p_user -> id = 1 ; p_user -> pwd = "XXX" ; p_user -> mailAddress = "XXXX@XXX.com" ; } void func2(st_User *p_user) { /* ユーザー名を登録 */ (*p_user).name = "鈴木"; (*p_user).id = 2 ; (*p_user).pwd = "YYY" ; (*p_user).mailAddress = "YYYY@YYY.com" ; } |
上記のfunc2関数では”->”(アロー演算子)を使わず、”*”(アスタリスク)を使って参照しています。
このように書くことは出来ますが、正直見にくいですし、”->”(アロー演算子)の方がスッキリして分かりやすいです。
なので、「構造体は、ポインタになると変数の参照が分かりにくいから”->”(アロー演算子)で書くんだ!」という理解をしておけば、万が一忘れても思い出せるはずです。
構造体の配列の使い方(よく使う)
構造体を配列として使用することも出来ます。
「構造体配列」と個人的には呼んでいて、構造体配列を使用することも多いです。
構造体が配列になっただけですので、特にここは難しく理解する必要はありません。メンバ変数の参照方法も構造体と変わりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include<stdio.h> /* User型の構造体宣言 */ typedef struct User { char *name ; //ユーザー名 int id ; //ID char *pwd ; //パスワード char *mailAddress ; //メールアドレス }st_User; void func(int *ptr); int main() { int i; st_User UserList[3] ={ {"田中", 1, "XXX", "XXXX@XXX.com"}, {"鈴木", 2, "YYY", "YYYY@YYY.com"}, {"佐藤", 3, "ZZZ", "ZZZZ@ZZZ.com"} } ; for(i = 0; i < 3; i++) { printf("名前...%s / ID...%d / パスワード...%s / メールアドレス...%s\n",UserList[i].name,UserList[i].id,UserList[i].pwd,UserList[i].mailAddress); } return 0; } |
上記サンプルコードのように、構造体配列を使って、データのリストのようなものを作成することもあります。
初見で見たときに、「何だこの配列は。。」とビックリすることもあると思いますが、多分それは構造体配列です。
構造体配列の型をGrepとかで調査していくと、最終的に構造体の定義にたどり着くと思うので、見かけたらソースコードを追ってみてください。
最後に
構造体を難しく理解しすぎてややこしくなっている新人が多いイメージですが、そこまで難しくはありません。
構造体は慣れてしまえば、コードを書くときに本当に便利になります。
逆に言えば、構造体を使用しないと、ものすごくややこしいコードになってしまいますので、頑張って理解しましょう!