つくる 可変人数対応
この章で手に入れたもの
第2章では、変数がどこに置かれ、いつ消えるかを見てきました。関数を抜けると消えるスタック、プログラムが終わるまで残る静的領域、そして自分で借りて自分で返すヒープです。malloc と free、0埋めの calloc、あとから伸ばす realloc も使えるようになりました。
今回はそれを1本にまとめて、成績管理を何人でも扱える形に作り替えます。入門で書いた int scores[100]; を捨てて、人数を読んでから必要なぶんだけ借ります。
名前の置き場所をどうするか
点数は int なので malloc(sizeof(int) * n) で済みます。悩むのは名前です。文字列は長さがばらばらで、1人あたり何バイト要るかは読んでみるまで分かりません。
長さぴったりに確保する方法は次の章で扱います。今回は1人あたりの上限を32バイトと決めて、その32バイトが n 人ぶん並んだ1本の領域を借ります。
#define NAME_MAX 32
char *names = malloc((size_t)NAME_MAX * n);こうすると、i 番目の人の名前の先頭は names + i * NAME_MAX です。第1章のポインタ演算がそのまま出てきました。char は1バイトなので、+ i * NAME_MAX は「i 個ぶん先の32バイト区画の先頭」を指します。読み込みも表示も、このアドレスをそのまま渡すだけです。
scanf("%31s", names + i * NAME_MAX);%31s の 31 は、終端の \0 を入れる1バイトを残した数です。上限を指定せずに %s と書くと、長い名前が来たときに隣の区画を壊します。この危険は第3章で正面から扱いますが、今のうちから幅を書く癖を付けてください。
組み立ての順番
やることは6つです。
- 人数を読む
- 名前用と点数用の領域を確保して、両方とも
NULLでないか確かめる - 人数ぶんだけ名前と点数を読む
- 一覧を表示しながら、合計と最高点の人を求める
- 合計と平均と最高を表示する
- 借りた2本を返して
NULLを入れる
出力の形
人数が3で、田中82点、鈴木91点、佐藤75点のとき、次のようになります。
=== 成績管理 ===
人数 3
田中 82点
鈴木 91点
佐藤 75点
合計 248点
平均 82.7点
最高 鈴木 91点平均は %.1f です。248 / 3 は整数どうしの割り算で切り捨てになるので、割る前に (double) を付けてください。最高点は点数そのものではなく何番目の人かを覚えておくと、名前と点数の両方を一度に出せます。
詰まったときの見方
確保に失敗する経路は今回のテストでは通りませんが、確認は必ず書いてください。片方だけ成功したときに、もう片方を返さずに抜けるとリークになります。前回の数え上げを思い出すと、返す回数が足りないことに気付けます。
要件
- 名前は1人あたり NAME_MAX バイトの区画が n 個並んだ1本の領域を malloc で確保する
- 点数は sizeof(int) * n で確保する
- 確保に失敗したら「確保に失敗しました」を出し、借りた側を返してから return 1 する
- 名前は %31s のように幅を指定して読む
- 平均は %.1f、最高は名前と点数の両方を出す
- 最後に2本とも free して NULL を入れる
入出力例
main("3
田中 82
鈴木 91
佐藤 75") → "=== 成績管理 ===
人数 3
田中 82点
鈴木 91点
佐藤 75点
合計 248点
平均 82.7点
最高 鈴木 91点"
main("5
田中 60
鈴木 71
佐藤 80
高橋 90
伊藤 95") → "=== 成績管理 ===
人数 5
田中 60点
鈴木 71点
佐藤 80点
高橋 90点
伊藤 95点
合計 396点
平均 79.2点
最高 伊藤 95点"