つくる 可変人数対応

この章で手に入れたもの

第2章では、変数がどこに置かれ、いつ消えるかを見てきました。関数を抜けると消えるスタック、プログラムが終わるまで残る静的領域、そして自分で借りて自分で返すヒープです。mallocfree、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つです。

  1. 人数を読む
  2. 名前用と点数用の領域を確保して、両方とも NULL でないか確かめる
  3. 人数ぶんだけ名前と点数を読む
  4. 一覧を表示しながら、合計と最高点の人を求める
  5. 合計と平均と最高を表示する
  6. 借りた2本を返して NULL を入れる

出力の形

人数が3で、田中82点、鈴木91点、佐藤75点のとき、次のようになります。

=== 成績管理 === 人数 3 田中 82点 鈴木 91点 佐藤 75点 合計 248点 平均 82.7点 最高 鈴木 91点

平均は %.1f です。248 / 3 は整数どうしの割り算で切り捨てになるので、割る前に (double) を付けてください。最高点は点数そのものではなく何番目の人かを覚えておくと、名前と点数の両方を一度に出せます。

詰まったときの見方

確保に失敗する経路は今回のテストでは通りませんが、確認は必ず書いてください。片方だけ成功したときに、もう片方を返さずに抜けるとリークになります。前回の数え上げを思い出すと、返す回数が足りないことに気付けます。

要件

  1. 名前は1人あたり NAME_MAX バイトの区画が n 個並んだ1本の領域を malloc で確保する
  2. 点数は sizeof(int) * n で確保する
  3. 確保に失敗したら「確保に失敗しました」を出し、借りた側を返してから return 1 する
  4. 名前は %31s のように幅を指定して読む
  5. 平均は %.1f、最高は名前と点数の両方を出す
  6. 最後に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点"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

つくる 可変人数対応

⌘S で保存