つくる ポインタ走査版
入門の成績管理をポインタで書き直す
このコースの通し課題は、入門で作った成績管理 CLI を作り替えていくことです。最終形は連結リスト版ですが、そこへ一足飛びには行きません。まずは中身を固定配列のまま、集計の書き方だけをポインタ走査に入れ替えます。
書き換えても出力は変わりません。それでよいのです。動きを変えずに書き方だけ変える練習は、実務のリファクタリングそのもので、しかも今回はこのあと配列を丸ごと動的メモリに差し替えるための下ごしらえになります。集計側がポインタで書けていれば、第2章で配列を malloc の領域に替えたとき、集計の関数は 1 行も直さずに動きます。配列でもヒープでも、先頭の番地と個数さえあれば走査できるからです。
名前と点数を 2 本の配列で持つ
いまはまだ構造体を使いません。名前の配列と点数の配列を並べて持ち、同じ添字が同じ学生を指す形にします。
char names[MAX][NAME_LEN];
int scores[MAX];同じ添字が同じ学生を指す、という約束はプログラムのどこにも書かれていません。片方だけを並べ替えたり、片方だけを詰めたりした瞬間に対応が壊れます。第4章で構造体にまとめるのは、この壊れやすい約束をコンパイラに守らせるためです。いまはまだ壊れやすいまま進み、なぜまとめたくなるのかを先に体験しておきます。
この持ち方だと、点数側をポインタで走査したときに「いま何番目か」が要ります。前回やったとおり、位置は差で取り出せます。
最高点は位置で覚える
最高点を値で持つと、誰の点数だったかが分かりません。最高点を指すポインタを持ち歩けば、値も位置も両方取れます。
int *best = scores;
for (int *p = scores; p < scores + n; p++) {
if (*p > *best) {
best = p;
}
}
printf("%s さん %d 点\n", names[best - scores], *best);best - scores がそのまま名前側の添字になります。ポインタどうしの差が要素数で返る、という規則がここで効いています。
同点が複数いた場合は、> で比べているので 先に現れたほうが残ります。>= にすると後のほうが残ります。どちらが正しいということはありませんが、どちらの動きなのかを意識せずに書くと、あとで結果が合わない原因になります。今回は先に現れたほうを最高点とします。
入力の形
1 行目に人数 n、続く n 行に名前と点数を空白区切りで置きます。人数を先に受け取る形は、第2章で malloc に渡す個数としてそのまま使います。今回は上限つきの固定配列なので、n が上限を超えないことは前提にしてかまいません。
では、集計部分をポインタ走査で書いてみましょう。
要件
- 1 行目の人数を読み、続けて名前と点数を人数分読み込む
- 合計と最高点の探索は scores 上のポインタ走査で書く
- 最高点は値ではなく、最高点を指すポインタで持つ
- 最高点が同じ学生が複数いる場合は先に現れたほうを採用する
- 出力は「人数は3人です」「合計は255点です」「平均は85.0点です」「最高点はTanakaさんの95点です」の形にする
入出力例
main("3
Tanaka 95
Suzuki 80
Yamada 80") → "人数は3人です
合計は255点です
平均は85.0点です
最高点はTanakaさんの95点です"
main("4", "Tanaka 60", "Suzuki 88", "Yamada 88", "Kato 44") → "人数は4人です
合計は280点です
平均は70.0点です
最高点はSuzukiさんの88点です"
main("2
Kato 100
Mori 50") → "人数は2人です
合計は150点です
平均は75.0点です
最高点はKatoさんの100点です"
main("1
Mori 73") → "人数は1人です
合計は73点です
平均は73.0点です
最高点はMoriさんの73点です"