コース一覧
    C言語中級 連結リスト・応用
    リスト版を完成させる

    C言語中級 連結リスト・応用

    C言語の連結リストと応用的なデータ操作を学び、実装でデータを扱うコースです。

    1
    連結リスト
    01. 配列の限界15分
    02. ノードを定義する15分
    03. 先頭に追加する15分
    04. リストを歩く15分
    05. 任意位置の挿入と削除15分
    06. リストを全解放する15分
    07. つくる リスト版成績管理15分
    08. 第5章クイズ10分
    2
    応用
    01. 動的な二次元配列15分
    02. コマンドライン引数15分
    03. バイナリ保存15分
    04. エラーの調べ方15分
    05. つくる 引数とバイナリ保存15分
    06. 第6章クイズ10分
    3
    総合制作
    01. リスト版を完成させる20分
    02. メモリバグ演習20分
    03. 自由拡張20分
    04. 完成と次のステップ10分

    リスト版を完成させる

    リスト版を1本にまとめる

    第5章で先頭挿入・走査・任意位置の削除・全解放を、第6章で引数とバイナリ保存を書いてきました。部品はもう揃っています。今回はそれらを 1 つの main の下にまとめて、番号でメニューを選ぶ成績管理CLI のリスト版を完成させます。入門で作った配列版と同じ操作ができて、中身だけが連結リストに入れ替わっている状態が目標です。

    配列版との違いは 2 つあります。ひとつは上限が無いこと、もうひとつはデータの持ち主がヒープに移ったことです。配列版は Student list[10] と書いた時点で 10 人が上限で、そのメモリは main が生きている間ずっと確保されたままでした。リスト版は登録するたびに malloc でノードを 1 つ借り、削除するたびに返します。何人でも入る代わりに、借りたものを返す責任がこちらに移ります。

    head を返す形でそろえる

    リストを触る関数の書き方は、head が変わりうるかどうか で決まります。先頭を削除したときや、空のリストに最初の 1 人を足したときは、head そのものが別のノードを指すことになります。関数の中で仮引数に代入しても呼び出し元の変数は変わらないので、新しい head を戻り値で返して head = delete_by_name(head, name); のように受け直します。

    ポインタのポインタを渡す書き方もありますが、戻り値で返すほうが読む人にとって分かりやすい利点があります。呼び出し側の 1 行を見ただけで、この関数は head を付け替えることがあると読み取れるからです。

    確保が2回なら解放も2回

    今回のノードは名前を char *name で持ちます。第3章でやったとおり、名前は入力された長さに合わせて確保します。つまりノードを 1 つ作るたびに、ノード本体と名前の文字列で 確保が 2 回 起きています。ということは、ノードを 1 つ捨てるときも解放は 2 回必要です。free(cur) だけを書くと、名前のぶんが誰からも参照されないままヒープに残ります。

    順序も決まっています。free(cur) を先に書いてしまうと、そのあと cur->name を読む手段が消えます。中の名前を先に解放してから、器であるノードを解放します。

    メニューは番号を読むだけ

    メニューの骨は入門で書いたものと同じです。番号を 1 つ読み、その番号で処理を選び、また番号を読む。終了番号が来たらループを抜けます。scanf の戻り値は読み取れた項目の数なので、scanf("%d", &command) == 1 を条件に入れておくと、入力が尽きたときも無限ループになりません。中身がリストに変わっても、この枠は一切変わらないところが面白い点です。変わったのは、それぞれの分岐が呼ぶ関数の中だけです。

    終わり方まで仕様に入れる

    このプログラムは 0 が入力されたときと、入力が尽きたときの両方で終わります。どちらの終わり方でも、残っているノードを全部解放してから終わるようにします。今回は解放した件数を最後に表示する仕様にしました。登録した数と解放した数が合っているかを目で確かめられるので、リークしていないことがそのまま出力に現れます。

    要件

    1. ノードは char *name と int score と struct Node *next を持つ。starterCode の定義を変えない
    2. 番号を1つ読み、1 なら登録、2 なら一覧、3 なら集計、4 なら削除、0 なら終了する
    3. 1 は続けて名前と点数を読み、末尾に追加して「田中を登録しました」と出す
    4. 2 は登録順に「田中 80点」の形で1行ずつ出す。0人なら「学生がいません」
    5. 3 は「人数は3人です」「合計は245点です」「平均は81.7点です」の3行を出す。平均は %.1f。0人なら「学生がいません」
    6. 4 は続けて名前を読み、一致するノードを外して「田中を削除しました」と出す。無ければ「田中は見つかりません」
    7. 1 から 4 と 0 以外の番号は「その番号はありません」と出す
    8. ループを抜けたら残ったノードを全部解放し、「2件解放しました」「終了します」の2行を出す
    9. 確保した領域は名前ぶんも含めてすべて解放する。入力をうながす案内文は一切出さない

    ヒント

    delete_by_name は1つ前のノードを prev で覚えながら進みます。先頭が対象のときだけ prev が NULL のままなので、そこで head を付け替えます

    ノード1つを捨てるときの解放は2回です。free(cur->name) を先に、free(cur) をあとに書きます

    free_all は次のノードの番地を別の変数に控えてから解放します。解放したあとに cur->next を読むことはできません

    生田 陸人
    監修生田 陸人
    ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
    編集 ゆめさく編集部·公開 2026/08/07

    関連レッスン

    • メモリバグ演習

      リーク・二重解放・ダングリングを見つけて直せるようになります。

    • 自由拡張

      欲しい機能を自分で設計して足せるようになります。

    • 完成と次のステップ

      次に何を学ぶかを選べるようになります。

    このレッスンに出てくる用語

    意味があいまいなまま進んだ語は、ここから読み直せます。

    • リスト順序付きで複数の値を扱うデータ構造
    • 引数位置引数=順番で渡す。
    • 配列サイズ固定の同型データの集まり
    • 連結リスト各要素が次の要素へのポインタを持つ構造。
    • ヒープ親が子より常に大きい(最大ヒープ)または小さい(最小ヒープ)木構造。
    • メモリプログラムとデータを一時保持する高速領域
    • 関数処理に名前を付けて再利用できる単位
    • 変数データに名前をつけて参照する仕組み
    main.c
    エディタを読み込んでいます

    メモ

    リスト版を完成させる

    ⌘S で保存