動的な構造体配列
1 人ぶんが借りられるなら、n 人ぶんも借りられる
前回は malloc(sizeof(Student)) で構造体を 1 つ借りました。第 2 章で int を n 個借りたときと同じ理屈で、構造体も並べて借りられます。
Student *list = malloc(sizeof(Student) * n);これで Student が n 個ぶん、すきまなく連続して並んだ領域が手に入ります。連続していることが効いてきて、list は先頭の Student を指すポインタなので、そのまま添字が使えます。
list[0].score = 80;
list[1].score = 95;list[i] は *(list + i) の別の書き方です。list + i は「i バイト先」ではなく「Student i 個ぶん先」に進むので、構造体の大きさを自分で掛ける必要はありません。第 1 章のポインタ演算がそのまま構造体にも効いている、というだけの話です。
ドットか矢印かは、やはり左側で決まる
list[i] は実体なのでドット、list + i はポインタなので矢印です。どちらで書いても同じ場所を触ります。
list[i].score = 80; /* 左は実体 */
(list + i)->score = 80; /* 左はポインタ */読みやすいほうを選んでかまいません。一覧を順に処理するときは添字のほうが素直なことが多いです。
free は 1 回でよい
n 人ぶんを 1 回の malloc で借りたなら、返すのも free(list) の 1 回だけです。malloc は借りた大きさを内部で覚えているので、こちらが n を伝える必要はありません。逆に free(&list[3]) のように途中の番地を渡してはいけません。free に渡してよいのは malloc が返したその番地だけです。
前回のように Student を 1 人ずつ別々に malloc していたら、話は変わります。そのときは人数ぶん free が必要です。「何回借りたか」と「何回返したか」を合わせる、という数え方で考えてください。
人数を先に読む形にする
入力の 1 行目に人数を置き、その数だけ確保してから読み込むと、何人でも同じコードで動きます。固定長配列のときのように #define MAX 100 を置いて、それを超えた入力を切り捨てる必要はもうありません。
int n;
scanf("%d", &n);
Student *list = malloc(sizeof(Student) * n);なお n が 0 以下だと malloc に 0 や負の値を渡すことになります。実務では読み込んだ直後に範囲を確かめますが、今回の課題では 1 以上の値しか来ない前提で進めます。
要件
- 人数を読んでから malloc で人数分の Student を確保する
- 確保に失敗したら「確保できませんでした」と表示して return 1 する
- 全員を「名前 点数」の形で1行ずつ表示する
- 最後に「平均 」に続けて平均点を小数第1位まで表示する
- 確保した領域を free する
入出力例
main("3
佐藤 80
鈴木 95
田中 71") → "佐藤 80
鈴木 95
田中 71
平均 82.0"
main("2
高橋 60
伊藤 91") → "高橋 60
伊藤 91
平均 75.5"