つくる 名前入力の安全化

章の道具を1本の入力処理にまとめる

第3章でやってきたことを並べ直します。終端まで数える my_strlen、1バイトずつ写す my_strcpy、器の広さを渡してあふれさせない safe_copy、カンマで切る分解、長さぶんだけ確保する my_strdup。この5つは、実はどれも「外から来た1行を安全に取り込む」という1つの仕事の部品です。今回はそれを組み上げます。

入力は 名前,点数 の行です。外から来るものなので、次のどれが来ても壊れてはいけません。

  • 想定より長い名前
  • カンマが無い行
  • 空行

切り詰めてから確保する順序

今回の要点は、safe_copymy_strdupこの順番で 使うところです。

char fixed[NAME_MAX + 1]; cut[n] = safe_copy(fixed, NAME_MAX + 1, line); names[n] = dup_name(fixed);

まず幅の決まった一時置き場 fixed に、あふれない形で受けます。ここで長さの上限が確定します。そのあと、確定した長さぶんだけヒープに確保して写します。順番が逆だと意味がありません。長さ制限をかける前に確保してしまうと、いくらでも長い名前をそのまま受け入れることになり、上限を設けた意味が消えます。

一時置き場が固定長でよいのは、そこには決して上限を超えて書き込まないからです。固定長そのものが悪いのではなく、広さを見ずに書き込むことが悪い、という区別がここではっきりします。

捨てた情報を黙って捨てない

safe_copy は切り詰めたかどうかを返します。この戻り値を捨ててしまうと、YamamotokurosawaYamamoto になったことを誰も知らないまま一覧に載ります。あとから別人と区別が付かなくなっても、原因を追う手掛かりが残りません。今回は切り詰めた行に印を出して、データが加工されたことを表に出します。

壊れた行は読み飛ばす

カンマが無い行や空行が来たときに落ちる作りだと、ファイル1行の乱れでプログラム全体が止まります。分解の前に line[pos]'\0' かどうかを見て、区切りが見つからなければその行は数えずに次へ進みます。人数 n を増やさないので、平均の計算にも影響しません。

最後に、確保した回数と解放した回数を出して、数が合っていることを確かめます。第2章で覚えた数え方が、そのまま今回の検算になります。読み飛ばした行では dup_name を呼んでいないので、確保の回数は一覧に出た人数と一致するはずです。ここがずれたら、読み飛ばしの位置か人数の数え方のどちらかが間違っています。

平均の割り算

人数で割るときは、整数どうしの割り算にならないよう気をつけます。total / n はどちらも int なので小数が切り捨てられます。(double)total / n と片方を実数にしてから割り、%.1f で桁を決めて出します。入門でやった話ですが、動的メモリの処理に気を取られていると落としやすいところです。

では、第3章の総まとめとして、あふれない名前入力を書き上げましょう。

要件

  1. fgets で1行ずつ読み、行末の改行を取り除く
  2. 空行と、カンマが無い行は読み飛ばして人数に数えない
  3. 名前は safe_copy で8文字までに受けてから、dup_name で必要な長さだけ確保して保持する
  4. 一覧は Tanaka 85点 の形。切り詰めた行だけ Yamamoto 70点 切り詰め の形にする
  5. 続けて 3人 平均81.7点 の形で、平均を小数第1位まで表示する
  6. 最後に全部 free して 確保3回 解放3回 の形で表示する

入出力例

main("Tanaka,85 Yamamotokurosawa,70 Sato,90") → "Tanaka 85Yamamoto 70点 切り詰め Sato 903人 平均81.7確保3回 解放3回" main("Ito,100 Nakamura,60 Kobayashidaisuke,80 Kato,75") → "Ito 100Nakamura 60Kobayash 80点 切り詰め Kato 754人 平均78.8確保4回 解放4回" main("Suzuki,64 Mori Aoki,72") → "Suzuki 64Aoki 722人 平均68.0確保2回 解放2回"

ヒント

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

メモ

つくる 名前入力の安全化

⌘S で保存