つくる 動的Student完成
この章でやったことを 1 つにまとめる
第 4 章では、構造体をポインタで触る ->、構造体をヒープに置く malloc(sizeof(Student))、人数ぶんまとめて借りる動的な構造体配列、表示のしかたを差し替える関数ポインタ、そして qsort による並べ替えを見てきました。今回は全部を 1 つのプログラムに入れて、成績管理の一覧部分を作り直します。
作るもの
入力は次の形で来ます。1 行目が表示のしかた、2 行目が人数、そのあとに人数ぶんの 名前 点数 が並びます。
1
4
Sato 80
Suzuki 95
Tanaka 80
Ito 73やることは 4 つです。人数ぶんの Student をヒープに確保して読み込み、点数の高い順に並べ替え、指定された形式で全員を表示し、最後に平均を小数第 1 位まで出します。表示のしかたは 1 なら 名前 点数、2 なら名前のあとに 点数 / 10 個の * を並べた棒グラフです。
並びを一意にしておく
上の例では Sato と Tanaka が同点です。点数だけで比べると、この 2 人のどちらが先に出るかは決まりません。前回書いたように、点数が等しいときは名前で比べて、並びが一意になるところまで決めてください。この例では Sato が先です。
表示の選び方
if で表示を分けるのではなく、先に関数ポインタへどちらかを入れてから、ループの中では選んだものを呼ぶ形にしてください。
void (*style)(const Student *) = print_plain;
if (mode == 2) {
style = print_bar;
}こうしておくと、ループの中に判定が残りません。表示の種類が増えても、増えるのは if の枝だけでループは変わりません。
平均の出し方
点数は int なので、そのまま足して n で割ると小数が切り捨てられます。片方を double にしてから割ってください。表示は %.1f にします。%f のままだと小数第 6 位まで出ます。
printf("平均 %.1f\n", (double)sum / n);後始末
malloc は 1 回なので free も 1 回です。表示や並べ替えが終わったあと、return の前に解放します。プログラムが終われば OS が回収してくれますが、この形をそのまま関数に切り出したときにリークが残るので、いまのうちから書く癖を付けておいてください。
入門の成績管理と何が変わったか
入門で書いた版は Student list[100]; のような固定長配列でした。100 人を超える入力は受け取れず、逆に 3 人しか使わない日でも 100 人ぶんの領域を抱えていました。人数の上限がコードに焼き付いていたわけです。
今回の版には上限がありません。人数は実行時に決まり、必要なぶんだけ借ります。並べ替えも自分で書かず、大小の決め方だけを渡して qsort にやらせています。表示のしかたも走査から切り離しました。同じ「成績を一覧する」という仕事でも、どこを実行時に決められるようにするかで、コードの形はここまで変わります。
残っている弱点は、途中に 1 人だけ挿入したいときです。動的配列は連続して並んでいるので、間に入れるには後ろを全部ずらすことになります。次の第 5 章では、ここを付け替えだけで済ませる連結リストに作り替えていきます。
要件
- 人数分の Student をヒープに確保して読み込む
- 確保に失敗したら「確保できませんでした」と表示して return 1 する
- qsort で点数の高い順に並べ替える。同点なら名前の辞書順にする
- 1行目が 2 なら print_bar、それ以外なら print_plain を関数ポインタに入れて使う
- 最後に「平均 」に続けて平均点を小数第1位まで表示する
- 確保した領域を free する
入出力例
main("1
4
Sato 80
Suzuki 95
Tanaka 80
Ito 73") → "Suzuki 95
Sato 80
Tanaka 80
Ito 73
平均 82.0"
main("2
3
Mori 88
Aoki 60
Endo 60") → "Mori ********
Aoki ******
Endo ******
平均 69.3"