構造体配列
2本の配列を1本にたたむ
第6章で作った成績管理CLI は、こういう形でした。
char names[3][32];
int scores[3];名前の表と点数の表が別々に並んでいて、同じ番号どうしが対応する約束でした。構造体を覚えた今なら、これを 1 本にできます。
struct Student list[3];list は「struct Student が 3 個並んだ配列」です。list[0] が 1 人目まるごと、list[1] が 2 人目まるごとを指します。名前と点数が同じ箱に入っているので、対応がずれる余地がそもそもありません。
添字とドットを続けて書く
メンバに触るときは、添字で人を選んでからドットでメンバを選びます。
list[1].score = 90;
printf("%s\n", list[1].name);list[1].score は「list の 2 人目の中の score」です。[] と . は左から順に働くので、この順番で書けば意図どおりに読まれます。list.score[1] のように逆さに書くとエラーになりますが、これは配列とメンバの役割を取り違えたときに出やすい間違いです。
入力も同じ調子で書けます。& の付け方は前回と変わりません。
scanf("%31s", list[i].name);
scanf("%d", &list[i].score);list[i].name は配列なので & なし、list[i].score は int の値なので & あり、という区別です。
ループとの相性がよくなる
1 本にたたんだいちばんの効果は、ループが読みやすくなることです。
for (i = 0; i < 3; i++) {
printf("%s %d点\n", list[i].name, list[i].score);
}2 本の配列を持っていたときは、ループの中で names[i] と scores[i] の 2 つを常に見比べる必要がありました。今は list[i] という 1 人分を取り出すだけで済みます。
この形は、あとで「点数の高い順に並べ替える」ような処理を書くときにも効いてきます。list[0] と list[1] を入れ替えれば、名前も点数もセットで動くからです。前回学んだとおり構造体は代入でまるごとコピーできるので、入れ替えも tmp = list[0]; list[0] = list[1]; list[1] = tmp; の 3 行で済みます。名前の配列と点数の配列が別だったころは、名前のほうを strcpy で、点数のほうを代入で、と 2 通りのやり方を混ぜる必要がありました。
人数の上限は自分で決める
struct Student list[3] と書いた時点で、入る人数は 3 人までに固定されます。配列の枠を超えて list[3] に書き込んでも、コンパイラは何も言わないまま関係ないメモリを壊します。何人まで扱うかを先に決め、ループの条件をその数と必ず揃えておくことが、そのまま安全対策になります。
では、3 人分を読み込んで一覧と平均を出しましょう。
要件
- struct Student list[3] を使う
- for ループで3人分の名前と点数を読み込む
- 一覧は「田中 82点」の形で1人1行ずつ表示する
- 最後に「平均は81点です」の形で平均を表示する
入出力例
main("田中 82", "佐藤 90", "鈴木 71") → "田中 82点
佐藤 90点
鈴木 71点
平均は81点です"
main("田中 60", "佐藤 70", "鈴木 80") → "田中 60点
佐藤 70点
鈴木 80点
平均は70点です"