C言語入門|8章の練習問題

8章では 「関数」 をテーマに、プログラムを部品化して扱いやすくする方法を学びました。
関数は C 言語の学習の中でも、特に「可読性」「再利用性」を大きく高めてくれる頼もしい存在です。

ただ、「関数は難しそう…」という声もよく聞きます。
そこでこの序章では、関数の役割や呼び出しの仕組みを 図解や表を使って直感的に理解できるように 解説していきます。

関数とは?

関数は「何か1つの仕事をまとめた小さなプログラム」のことです。

たとえば、

  • 画面にメッセージを表示する。
  • 数値を受け取って計算する。
  • 判定を行って結果を返す。

など、「1つの役割」を持つものを関数として切り出します。

■ 関数のイメージ(表で理解)

要素役割
関数名部品の名前。呼び出すときに使う
引数関数に渡すデータ
戻り値関数が返す結果
本体実際の処理({} の中)

以下のような箱イメージで考えると、とても理解しやすくなります。

関数名:calcPrice
引数:数量、単価
処理:数量 × 単価
戻り値:合計金額

「calcPrice(quantity, unitPrice)」のように箱に値を渡すと、
処理が行われて、戻り値が“ポンッ”と返ってきます。

関数定義の書式(表でまとめ)

8章で登場する命令(関数定義)の基本形は次の通りです。

書式説明
戻り値の型 関数名(引数リスト) { 処理 }関数を定義する基本形

void hello(void)
{
    printf("こんにちは!プログラムからのメッセージです。\n");
}
  • 戻り値がないときは void
  • 引数がないときも void
  • 処理内容は {} の中に書く

関数呼び出しの書式

呼び出すときは、たったこれだけです。

書式説明
関数名(引数);関数を実行(呼び出す)

hello();

これだけで定義済みの関数が動きます。

関数が呼び出される流れ

手順内容
① main から関数を呼ぶcalcArea(10.0)
② 値が引数に入るradius = 10.0
③ 関数内の処理を実行面積を計算
④ 結果が戻り値として返るreturn area;
⑤ main で受け取るdouble a = calcArea(10.0);

関数を使うメリット

メリット説明
コードが読みやすくなる。main がスッキリする。
再利用できる。同じ処理を何度も書かずに済む。
修正が楽になる。関数だけ直せば全部に反映される。
役割が明確になる。「何をするコードか」が名前でわかる。

8章の練習問題

ここからは「8章で学んだ関数の基礎〜応用」を実践的に身につけるための練習問題です。
引数・戻り値・条件分岐・関数分割・関数修正ポイント・再帰の理解などをバランスよく扱っています。

【練習8-1】(関数定義:基本)

次の仕様を満たす関数を定義してください。
処理内容は書かなくてよいものとします。

(1) 「こんにちは」「こんばんは」など、時間帯に応じた挨拶を表示する greet 関数

  • 引数:なし
  • 戻り値:なし

(2) 三角形の底辺と高さを受け取り、面積を返す calcTriangleArea 関数

  • 引数:double 型を2つ
  • 戻り値:double 型

(3) 現在のCPU時間(clock() 関数の戻り値)を long 型で返す getCpuTime 関数

  • 引数:なし
  • 戻り値:long 型

(4) 引数として int 型の点数を受け取ると、合否判定(60点以上で合格)を返す isPass 関数

  • 引数:int 型
  • 戻り値:bool 型

【解答例】

(1)
void greet(void);

(2)
double calcTriangleArea(double base, double height);

(3)
long getCpuTime(void);

(4)
bool isPass(int score);

【解説】

  • 戻り値・引数の型が正しく書けているかがポイント
  • 処理内容(本体)は不要なので 書式の理解 が重要
  • 関数名は “何をするか” が分かるように命名している。

【練習8-2】(条件判定関数の完成)

次の仕様に従って、練習8-1 (4) の isPass 関数を完成させ、
この関数を呼び出す main 関数を作成してください。

【合否判定ルール】

  • 60 点以上 → 合格
  • 60 点未満 → 不合格

【表示例】

  • 合格だった場合:
     「75 点は合格です。」
  • 不合格だった場合:
     「42 点は不合格です。」

【解答例】

#include <stdio.h>
#include <stdbool.h>

bool isPass(int score)
{
    return score >= 60;
}

int main(void)
{
    int score = 75;

    if (isPass(score)) {
        printf("%d 点は合格です。\n", score);
    } else {
        printf("%d 点は不合格です。\n", score);
    }

    return 0;
}

【解説】

  • isPass は「真偽を返す」ため return score >= 60; という書き方が最もスマート
  • 三項演算子でも書けるが、今回は if の方が読みやすい
  • main で関数を呼んで表示する流れが 8章の基本の形

【練習8-3】(関数修正ポイントを探す問題)

次のプログラムは「歩く → 電車に乗る → 走る → 走る」の順に出力します。

#include <stdio.h>

void train(void)
{
    printf("電車に乗ります。\n");
}

void jog(void)
{
    printf("走ります!\n");
}

void walk(void)
{
    printf("歩きます。\n");
}

int main(void)
{
    printf("出発!\n");
    walk(); train(); jog(); jog();
    printf("帰宅。\n");
    return 0;
}

このプログラムを 「走った後には必ず歩く」 ようにするために修正すべき点を 2つ 挙げてください。
ただし main の変更とプロトタイプ宣言は禁止とします。

【解答例】

修正すべき点はこれ

1.jog 関数の中で walk を呼び出すよう変更する。

void jog(void)
{
    printf("走ります!\n");
    walk();   // 走ったあとは歩く
}

2.walk 関数を jop より下に移動(または jog を walk より上に移動)する。
→ なぜなら、プロトタイプ宣言を使えないため 定義順に依存するから

【解説】

  • main は変更不可なので、呼び出しの順番を変えることはできない。
  • jog() の中に walk() を組み込むことで「走ったら歩く」を実現する。
  • プロトタイプなし → 呼び出す関数は 先に定義する必要がある
    → これに気づくことがポイント

【練習8-4】(類似問題:ポイント分配プログラム)

次の仕様を満たす 「ポイント分配プログラム」 を作成しなさい。
元の割り勘プログラムと同様に、処理を 3 つの関数に切り出し、main から呼び出す形に変更すること。

【条件】

  • 総ポイント(例:1260ポイント)をメンバー人数で割り、
    1人あたりの配布ポイントを整数単位で切り上げ て求める。
    → 小数点以下が発生したら必ず 1 ポイント上乗せする。
  • 配布後に端数が余った場合、
    余ったポイントはリーダーが受け取る。
  • 入力は文字列で受け取り、整数に変換する。

【作成する関数】(3 つ)

(1)iget 関数:入力 → int 変換

項目内容
戻り値入力された値(int 型)
引数なし
機能scanf で文字列入力し、atoi で整数に変換して返す

(2)calcPoint 関数:分配ポイントの計算

項目内容
戻り値1人あたりの配布ポイント(切り上げ後の int 型)
引数総ポイント、人数
機能小数点があれば切り上げて整数にする
1260 ÷ 11 → 114.545… → 115

(3)showResult 関数:結果表示

項目内容
戻り値なし
引数1人あたりポイント、余りポイント、総人数
機能結果を読みやすく表示する

【解答例】

#include <stdio.h>
#include <stdlib.h>

int iget(void)
{
    char buf[1024];
    scanf("%s", buf);
    return atoi(buf);
}

int calcPoint(int total, int members)
{
    double tmp = (double)total / members;

    int point = (int)tmp;        // 切り捨て
    if ((double)point < tmp) {
        point += 1;              // 小数点以下があれば切り上げ
    }

    return point;
}

void showResult(int point, int extra, int members)
{
    printf("*** ポイント配布結果 ***\n");
    printf("1人あたり %dpt(%d人)、リーダーは %dpt を受け取ります。\n",
           point, members - 1, extra);
}

int main(void)
{
    int total, members;
    int point, leader;

    printf("総ポイントを入力してください:");
    total = iget();

    printf("参加メンバー数を入力してください:");
    members = iget();

    point = calcPoint(total, members);

    leader = total - point * (members - 1);

    showResult(point, leader, members);

    return 0;
}

【解説】

iget 関数

  • 文字列として入力したものを atoi で整数化
  • 入力まわりを関数化すると main がスッキリする

calcPoint 関数

  • 割り算 → double
  • 切り捨て値よりも元の値が大きければ「小数点以下がある」
  • そのとき +1 して切り上げ
  • 100 円単位→ポイント単位に変わっただけでロジックは同じ

showResult 関数

  • 表示専用の関数にすることで
     「ロジックと表示の分離」 が学べる
  • 人数から 1 を引くのは、リーダー以外の人数のため

【練習8-5】(再帰関数の理解)

次の仕様を満たす 再帰関数 sumToN を定義してください。

【sumToN の仕様】

  • 引数 n を受け取り、
     1 〜 n までの合計を返す
  • 再帰を使って実装すること

sumToN(5) → 1+2+3+4+5 = 15

【解答例】

int sumToN(int n)
{
    if (n <= 1) return n;
    return n + sumToN(n - 1);
}

【解説】

  • 再帰は「自分自身を呼び出す関数」
  • 終端条件(停止条件) が必要
  • sumToN(n) = n + sumToN(n-1) という数学的定義そのまま
  • 再帰の“美しさ”を感じられる基本問題

【練習8-6】(実用ミニプログラム:温度変換関数)

次の仕様を満たす温度変換プログラムを作成してください。

【仕様】

  • double toFahrenheit(double c)
     → 摂氏 c を華氏に変換して返す
     (式:F = C × 9/5 + 32)
  • double toCelsius(double f)
     → 華氏 f を摂氏に変換して返す
     (式:C = (F - 32) × 5/9)
  • main で以下を実行・表示
     ・摂氏 30℃ を華氏へ
     ・華氏 98.6°F を摂氏へ

【解答例】

#include <stdio.h>

double toFahrenheit(double c)
{
    return c * 9.0 / 5.0 + 32.0;
}

double toCelsius(double f)
{
    return (f - 32.0) * 5.0 / 9.0;
}

int main(void)
{
    double f = toFahrenheit(30.0);
    double c = toCelsius(98.6);

    printf("30℃ = %.2f°F\n", f);
    printf("98.6°F = %.2f℃\n", c);

    return 0;
}

【解説】

  • 「入力 → 処理 → 戻り値」という関数の基本形が明確
  • 数学式をそのままコードにできる良い練習
  • 単体テストがしやすく、関数の役割がはっきりしている。