自由拡張

足す前に仕様を決める

ここまでで成績管理CLI のリスト版は一通り動くようになりました。ここからが本当の制作です。使ってみて足りないと思った機能を、自分で足します。

好きに作ってよいと言われて手が止まる人は多いはずです。理由ははっきりしていて、どこまでできれば完成なのかが決まっていない からです。書く前に決めることは 3 つあります。何を入力するか、何を出力するか、うまくいかなかったときどうするか。この 3 つが決まれば、あとは書くだけの作業になります。今回はその練習として、2 つの機能を仕様つきで用意しました。順位表と、平均以上の抽出です。

順位表はポインタの配列を作って並べる

リストは並べ替えが苦手です。ノードの next を付け替えて順序を変えることもできますが、登録順という情報が壊れてしまいます。楽なのは、ノードを指すポインタだけを集めた配列 を作り、その配列を並べ替える方法です。

struct Node **arr = malloc(count * sizeof(struct Node *));

struct Node ** は「struct Node へのポインタ」を指すポインタです。中身はノードの複製ではなく番地なので、いくら並べ替えてもリスト本体の next は 1 つも変わりません。表示が終わったら配列だけを解放します。ノードは解放しません。借りた配列の持ち主はこの関数ですが、ノードの持ち主はリストのままだからです。

並べ替えは第4章の qsort を使います。要素がポインタなので、比較関数に渡ってくるのは struct Node * を指すポインタ、つまり struct Node ** です。*(struct Node **)a と 1 段はがしてからメンバを読みます。

同点の順番は自分で決める

qsort は同点のときにどちらを前に置くか保証しません。手元で正しく見えても、別の環境では入れ替わることがあります。ですから 2 番目のキーまで決めて、順序が一意になるようにします。今回はノードに登録番号 id を持たせて、点数が同じなら id の小さいほうを上にします。

借りたものと借りていないもの

この回でいちばん間違えやすいのは、順位表を出したあとの後始末です。この関数の中で malloc したのは配列 1 本だけなので、返すのも配列 1 本だけです。配列の中に入っているのはノードの番地ですが、そのノードを作ったのはこの関数ではありません。リストがまだ使っているものを勝手に解放すれば、そのあとの一覧表示で解放済みの領域を読むことになります。

その領域を確保した人が解放する という持ち主の考え方を、ここで一度はっきりさせておきます。関数を書くときは、返す義務があるのはどれかを最初に決めます。決めておけば、あとから読む人も迷いません。

平均以上は割り算しないで比べる

平均以上の学生を選ぶとき、平均を double で求めてから比べたくなりますが、割り算をすると端数が出ます。score >= (double)total / count は多くの場合正しく動きますが、両辺に count を掛けた score * count >= total なら整数のままで、端数の心配がありません。表示のときだけ %.1f で小数にします。

要件

  1. 1 から 4 と 0 の動きは前回と同じ。starterCode に入っているので変えない
  2. 5 は点数の高い順に「1位 鈴木 95点」の形で全員を出す。0人なら「学生がいません」
  3. 5 で同点になったときは、登録が早いほう、つまり id の小さいほうを上にする
  4. 5 のあとで 2 の一覧を出しても、並びは登録順のままにする
  5. 5 で確保した作業用の配列は、表示が終わったら解放する。ノードは解放しない
  6. 6 は1行目に「平均は81.7点です」を出し、続けて平均以上の学生を登録順に「鈴木 95点」の形で出す。0人なら「学生がいません」
  7. 終了時は全ノードを解放し、「3件解放しました」「終了します」の2行を出す
  8. 入力をうながす案内文は一切出さない

入出力例

main("1 田中 80 1 鈴木 95 1 佐藤 70 5 6 0 ") → "田中を登録しました 鈴木を登録しました 佐藤を登録しました 1位 鈴木 952位 田中 803位 佐藤 70平均は81.7点です 鈴木 953件解放しました 終了します" main("1 田中 80 1 鈴木 80 1 佐藤 90 5 4 鈴木 5 6 2 0 ") → "田中を登録しました 鈴木を登録しました 佐藤を登録しました 1位 佐藤 902位 田中 803位 鈴木 80鈴木を削除しました 1位 佐藤 902位 田中 80平均は85.0点です 佐藤 90田中 80佐藤 902件解放しました 終了します" main("5 6 0 ") → "学生がいません 学生がいません 0件解放しました 終了します"

ヒント

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

メモ

自由拡張

⌘S で保存