練習:入門総合問題

プログラミングの学習は水泳のようなものです——教科書を読むだけでは不十分で、実際に水に入る必要があります。この回では最初の9回の内容をまとめ、3つの完全なミニプログラムを書いてもらいます。

プロジェクト1:摂氏→華氏変換

このプロジェクトは変数、算術演算、入出力を組み合わせます。変換公式は次の通りです。

F = C x 9 / 5 + 32

要件分析

重要ポイント

C
#include <stdio.h>

int main(void) {
    double celsius;
    printf("Enter temperature in Celsius: ");
    scanf("%lf", &celsius);

    double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
    double kelvin = celsius + 273.15;

    printf("\n===== Temperature Conversion =====\n");
    printf("Celsius:   %.2f C\n", celsius);
    printf("Fahrenheit:%.2f F\n", fahrenheit);
    printf("Kelvin:    %.2f K\n", kelvin);
    printf("==================================\n");
    return 0;
}
▶ 試してみよう
TEXT
Enter temperature in Celsius: 36.5

===== Temperature Conversion =====
Celsius:   36.50 C
Fahrenheit:97.70 F
Kelvin:    309.65 K
==================================
💡 ヒント: 9/5ではなく9.0 / 5.0と書いてください。整数除算9/5は1になり、1.8にはならないため、不正な結果になります。

よくある間違い

  1. 整数除算:celsius * 9 / 59/5が1に切り捨てられる → 9.0/5.0と書くこと
  2. doublescanf%fを使う:doubleには%lfが必要。printfではdouble%fが使える
  3. &の忘れ:scanf("%lf", celsius)はクラッシュする → scanf("%lf", &celsius)

拡張チャレンジ

逆変換もできますか?——華氏を入力して摂氏を出力してみましょう。公式はC = (F - 32) x 5 / 9です。

プロジェクト2:閏年判定

このプロジェクトは条件分岐、論理演算子、剰余演算子を組み合わせます。

閏年のルール

論理条件として表すと次のようになります。

C
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)

要件分析

重要ポイント

C
#include <stdio.h>

int main(void) {
    int year;
    printf("Enter a year: ");
    scanf("%d", &year);

    if (year <= 0) {
        printf("Error: Year must be a positive integer\n");
        return 1;
    }

    int is_leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    int feb_days = is_leap ? 29 : 28;

    printf("\n===== Result =====\n");
    printf("Year:     %d\n", year);
    printf("Type:     %s\n", is_leap ? "Leap year" : "Common year");
    printf("Feb days: %d\n", feb_days);
    printf("==================\n");
    return 0;
}
▶ 試してみよう
TEXT
Enter a year: 2000

===== Result =====
Year:     2000
Type:     Leap year
Feb days: 29
==================

境界となる年をいくつかテストしてみましょう。

期待結果 理由
2000 閏年 400で割り切れる
1900 平年 100で割り切れるが400では割り切れない
2024 閏年 4で割り切れるが100では割り切れない
2023 平年 4で割り切れない

段階的な導出

初心者は閏年の条件を間違えがちです。段階的に導いてみましょう。

  1. 第1段階:4で割り切れる → たぶん閏年
  2. 第2段階:しかし100で割り切れる世紀年は通常閏年ではない → 「かつ100で割り切れない」という条件を追加
  3. 第3段階:しかし400で割り切れる世紀年は閏年 → 「または400で割り切れる」という条件を追加
💡 ヒント: 段階的に導出するほうが公式を暗記するより確実です。複雑な条件は、まず簡単な版を書き、次第に修正を追加していきましょう。

プロジェクト3:シンプルな電卓

このプロジェクトはswitch-case、ループ、入出力、算術演算を組み合わせます。

要件分析

重要ポイント

C
#include <stdio.h>

int main(void) {
    double a, b;
    char op;
    char again;

    do {
        printf("\n===== Simple Calculator =====\n");
        printf("Enter expression (e.g. 3 + 5): ");
        scanf("%lf %c %lf", &a, &op, &b);

        printf("--------------------\n");
        switch (op) {
            case '+':
                printf("%.2f + %.2f = %.2f\n", a, b, a + b);
                break;
            case '-':
                printf("%.2f - %.2f = %.2f\n", a, b, a - b);
                break;
            case '*':
                printf("%.2f * %.2f = %.2f\n", a, b, a * b);
                break;
            case '/':
                if (b == 0) {
                    printf("Error: Division by zero!\n");
                } else {
                    printf("%.2f / %.2f = %.2f\n", a, b, a / b);
                }
                break;
            default:
                printf("Error: Unsupported operator '%c'\n", op);
                break;
        }
        printf("--------------------\n");

        printf("Continue? (Y/N): ");
        scanf(" %c", &again);
    } while (again == 'Y' || again == 'y');

    printf("Goodbye!\n");
    return 0;
}
▶ 試してみよう
TEXT
===== Simple Calculator =====
Enter expression (e.g. 3 + 5): 10 / 3
--------------------
10.00 / 3.00 = 3.33
--------------------
Continue? (Y/N): Y

===== Simple Calculator =====
Enter expression (e.g. 3 + 5): 5 / 0
--------------------
Error: Division by zero!
--------------------
Continue? (Y/N): N
Goodbye!

設計のポイント

  1. 入力形式scanf("%lf %c %lf", &a, &op, &b)で1回の呼び出しで3つの値を読み込みます。スペース区切りで簡潔かつ自然
  2. ゼロ除算の防止case '/'で先にb == 0を確認し、実行時エラーを回避
  3. ループ制御do-whileで少なくとも1回は計算を実行。ユーザーが継続するか選択
  4. バッファ処理scanf(" %c", &again)%c前のスペースが、前の入力の改行をスキップ

拡張チャレンジ

電卓に剰余演算子%を追加してみましょう(オペランドは整数である必要があります)。また、math.hpow関数を使った累乗のヒントも追加してみてください。

総合知識の復習

これら3つのプロジェクトは最初の9回の核心的な知識をカバーしています。

プロジェクト カバーする概念
温度変換 変数と型、算術演算、printf書式、scanf入力、整数除算の落とし穴
閏年判定 剰余、関係演算子、論理演算子、if-else、三項演算子、入力検証
シンプルな電卓 switch-case、do-whileループ、continue、バッファ処理、ゼロ除算チェック

良いプログラムを書く一般的な手順

  1. 要件を理解する:入力は何か?出力は何か?エッジケースは何か?
  2. 構造を選ぶ:どの文を使うか?ifかswitchか?forかwhileか?
  3. 擬似コードを書く:自然言語で論理を記述してからCに翻訳する
  4. 段階的に実装する:書きながらテストする。すべて書いてからデバッグしない
  5. 徹底的にテストする:正常値、境界値、無効値をテストする

❓ よくある質問

Q なぜscanfではdouble%lfが必要なのに、printfでは%fを使うのですか?
A scanfはメモリに正しく書き込むために仮引数の正確なサイズを知る必要があり、doublefloatはサイズが異なります。printfではfloatは自動的にdoubleに昇格されるため、%fで両方に対応できます。
Q 閏年の判定には、単一の論理式とネストされたif、どちらが良いですか?
A 単純な条件なら論理式のほうがコンパクトです。複雑な条件ならネストされたifのほうが読みやすいです。初心者はまずネストされたifで論理を理解し、それから論理式に簡略化するのが良いでしょう。
Q 浮動小数点数でb == 0と比較するのは信頼できますか?
A 厳密には浮動小数点数を==で比較すべきではありませんが、ユーザーが直接0を入力した場合は正確に0.0になります。より厳密な方法はfabs(b) < 1e-10ですが、これにはmath.hが必要です。
Q プログラムが途中で既に間違っている場合、どうやってデバッグしますか?
A 重要な箇所にprintfを追加して中間変数の値を出力し、エラーを段階的に絞り込みます。これは「printfデバッグ」と呼ばれ、最も基本的で効果的なデバッグ手法です。

📖 まとめ

📝 練習問題

  1. 温度プログラムに逆変換機能を追加してください。1で摂氏→華氏、2で華氏→摂氏を選択し、switchまたはif-elseを使います。
  2. 閏年プログラムを拡張し、開始年と終了年を受け付けて、その範囲のすべての閏年を出力するようにしてください(forループを使用)。
  3. 電卓に剰余%と整数除算を追加してください。剰余には整数オペランドが必要なため、ユーザーに整数の入力を促すようにします。
100%