今回は新しい考え方「構造体」の説明をします。
ずいぶん便利な仕組みなのですが、ここでつまづく方もいらっしゃるようです。
何が引っ掛かるのかよく分かりませんが、使い道が不明なのではないでしょうか?
使い道が分かれば、これほど便利な仕組みは無いと思います。
また、C++のクラスにも通じる考え方なので、非常に重要な項目だと思います。
「構造体」とは何をするものなのかというと、関連のあるデータをまとめる仕組みです。
データというのは、これまで使ってきた「変数」や「配列」などを指します。
複数のデータを1つにまとめることが出来るため、所属を明らかにすることが出来ます。
また、ずっと後に説明する関数でも威力を発揮します。
では、基本的な使い方について説明します。
※まずは文法のみ説明して、具体例は後から説明します。
基本的な文法は↓の通りです。
<構造体宣言>
struct タグ名 { メンバ変数宣言; メンバ変数宣言; ・ ・ ・ };
タグ名というのは「構造体」につける名前です。
メンバ変数というのは「変数」や「配列」のことで、ここで複数の「変数」や「配列」を宣言出来ます。
まずは、新しくこのような「構造体」を作ることを「宣言」します。
※「宣言」だけでは使えないので注意してください。
<構造体宣言の例>
struct Sample { int a; int b; char c[10]; }; |
実際にプログラムで使う際には「実体(インスタンス)」が必要となります。
「実体(インスタンス)」の作り方は↓の通りです。
<実体の作成>
struct タグ名 実体名;
これで実際に使える「構造体」が出来上がります。
<実体作成の例>
struct Sample sample; |
※sampleが実体として使用する名前です。
※タグ名と同じ名前にしなくても良いです。
実際のデータは「メンバ変数」に入っていますので、「メンバ変数」へのアクセス方法を示します。
例としてメンバ変数に0を代入するケースを書きます。
<メンバ変数へのアクセス>
実体名.メンバ変数名 = 0;
メンバ変数へのアクセスには「.(ドット演算子)」を使います。
<メンバ変数アクセスの例>
sample.a = 0; |
と書くと、実体sampleに所属しているaという変数に0を代入することになります。
これだけでは、まだまだ説明不足ですので、実際にプログラムの例を書いて説明します。
まとめるデータとして以下のものを用意しました。
ゲームのキャラクターデータのようなものです。
キャラクター名 char name[20]; ヒットポイント int hp 攻撃力 int attack 所持金 int gold
タグ名は「Character」にしましょう。
実体名は主人公を表す「hero」にします。
宣言を書き、実体を作り、データを代入するところまで書いてみます。
<sample program 100-01>
#include <stdio.h> #define MAX_NAME 20 /* 構造体宣言 */ struct Character { char name[MAX_NAME]; int hp; int attack; int gold; }; int main(void) { /* 実体の作成 */ struct Character hero; /* メンバ変数へのアクセス */ scanf("%s", hero.name); hero.hp = 100; hero.attack = 10; hero.gold = 0; return 0; } |
ゲームキャラクターのデータを初期化しているイメージですね。
ゲームのキャラクターデータということで、ついでに↓のことも説明します。
「構造体」は別のプログラム言語では「ユーザ定義型」と呼ばれることがあります。
「ユーザ」が自分で「定義」する「型」ということですね。
「型」というのはint型とかdouble型といったデータの入れ物のことです。
そして「構造体」も「ユーザ」が作った1つの型なので、「構造体」の「配列」も作ることが出来ます。
「構造体」の配列は色々な場面で使います。
例えば、上のサンプルでゲームのキャラクターの構造体を作りました。
この構造体を「配列」にするということは、キャラクター数人でパーティを組むというイメージになります。
プログラムを書いてみます。
パーティメンバーは3人ということにして、#defineでPARTYを3に設定します。
構造体の実体名もheroでは1人のイメージなので、playerという名前の配列にします。
メンバ変数へのアクセスも配列の添え字を指定して行います。
さらに表示のプログラムも加えてみました。
<sample program 100-02>
#include <stdio.h> #define MAX_NAME 20 #define PARTY 3 /* 構造体宣言 */ struct Character { char name[MAX_NAME]; int hp; int attack; int gold; }; int main(void) { /* 実体の作成 */ struct Character player[PARTY]; int i; /* メンバ変数へのアクセス */ for (i = 0; i < PARTY; i++) { scanf("%s", player[i].name); player[i].hp = 100; player[i].attack = 10; player[i].gold = 0; } for (i = 0; i < PARTY; i++) { printf("%s : HP %d : ATK %d : G %d\n", player[i].name, player[i].hp, player[i].attack, player[i].gold); } return 0; } |
<実行結果>
player1 ← scanfの入力 player2 ← scanfの入力 player3 ← scanfの入力 player1 : HP 100 : ATK 10 : G 0 player2 : HP 100 : ATK 10 : G 0 player3 : HP 100 : ATK 10 : G 0 続行するには何かキーを押してください・・・
ちなみに上と同じことを「構造体」を使わずに書くとこうなります。
<sample program 100-03>
#include <stdio.h> #define MAX_NAME 20 #define PARTY 3 int main(void) { char playerName[PARTY][MAX_NAME]; int playerHp[PARTY]; int playerAttack[PARTY]; int playerGold[PARTY]; int i; /* メンバ変数へのアクセス */ for (i = 0; i < PARTY; i++) { scanf("%s", playerName[i]); playerHp[i] = 100; playerAttack[i] = 10; playerGold[i] = 0; } for (i = 0; i < PARTY; i++) { printf("%s : HP %d : ATK %d : G %d\n", playerName[i], playerHp[i], playerAttack[i], playerGold[i]); } return 0; } |
<実行結果>
player1 ← scanfの入力 player2 ← scanfの入力 player3 ← scanfの入力 player1 : HP 100 : ATK 10 : G 0 player2 : HP 100 : ATK 10 : G 0 player3 : HP 100 : ATK 10 : G 0 続行するには何かキーを押してください・・・
このように、プレイヤーに必要な項目の数だけ配列が必要になり、多くなればなるほど分かりにくくなってきます。
「構造体」にしておくことで、データをまとめて管理出来るので、非常にすっきりと整理出来ます。
トランプの「数字」と「マーク」など、これまでのプログラムでも「構造体」でまとめられそうなものがありました。
これから先は、まとめられるデータは「構造体」でまとめるようにしましょう。