練習:入門総合問題
プログラミングの学習は水泳のようなものです——教科書を読むだけでは不十分で、実際に水に入る必要があります。この回では最初の9回の内容をまとめ、3つの完全なミニプログラムを書いてもらいます。
プロジェクト1:摂氏→華氏変換
このプロジェクトは変数、算術演算、入出力を組み合わせます。変換公式は次の通りです。
F = C x 9 / 5 + 32
要件分析
- 入力:摂氏の温度(小数の可能性あり)
- 出力:対応する華氏温度、小数第1位で丸め
- 拡張:ケルビン温度も出力(K = C + 273.15)
重要ポイント
double型で浮動小数点数を格納scanf("%lf")で倍精度浮動小数点数を読み込みprintf("%.1f")で小数桁数を制御- 算術式の演算子優先順位
例
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にはならないため、不正な結果になります。
よくある間違い
- 整数除算:
celsius * 9 / 5は9/5が1に切り捨てられる →9.0/5.0と書くこと doubleのscanfで%fを使う:doubleには%lfが必要。printfではdoubleに%fが使える&の忘れ:scanf("%lf", celsius)はクラッシュする →scanf("%lf", &celsius)
拡張チャレンジ
逆変換もできますか?——華氏を入力して摂氏を出力してみましょう。公式はC = (F - 32) x 5 / 9です。
プロジェクト2:閏年判定
このプロジェクトは条件分岐、論理演算子、剰余演算子を組み合わせます。
閏年のルール
- 4で割り切れ、かつ100で割り切れない → 閏年
- 400で割り切れる → 閏年
- それ以外 → 平年
論理条件として表すと次のようになります。
C
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
要件分析
- 入力:年を表す正の整数
- 出力:閏年か平年か、および2月の日数
- 防衛的チェック:無効な入力(負の数やゼロ)を処理
重要ポイント
- 剰余
%で割り切れるかを確認 - 論理演算子
&&、||で条件を組み合わせ if-else if-else多分岐構造- 入力の検証
例
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段階:4で割り切れる → たぶん閏年
- 第2段階:しかし100で割り切れる世紀年は通常閏年ではない → 「かつ100で割り切れない」という条件を追加
- 第3段階:しかし400で割り切れる世紀年は閏年 → 「または400で割り切れる」という条件を追加
💡 ヒント: 段階的に導出するほうが公式を暗記するより確実です。複雑な条件は、まず簡単な版を書き、次第に修正を追加していきましょう。
プロジェクト3:シンプルな電卓
このプロジェクトはswitch-case、ループ、入出力、算術演算を組み合わせます。
要件分析
- 入力:2つの数値と1つの演算子(+ - * /)
- 出力:計算結果
- 防衛的チェック:ゼロ除算時にメッセージを表示
- ループ:各計算後に継続するか確認
重要ポイント
switch-caseで演算子を処理do-whileで繰り返し計算- ゼロ除算のチェック
- フォーマット出力の桁揃え
continueで無効な操作をスキップ
例
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!
設計のポイント
- 入力形式:
scanf("%lf %c %lf", &a, &op, &b)で1回の呼び出しで3つの値を読み込みます。スペース区切りで簡潔かつ自然 - ゼロ除算の防止:
case '/'で先にb == 0を確認し、実行時エラーを回避 - ループ制御:
do-whileで少なくとも1回は計算を実行。ユーザーが継続するか選択 - バッファ処理:
scanf(" %c", &again)の%c前のスペースが、前の入力の改行をスキップ
拡張チャレンジ
電卓に剰余演算子%を追加してみましょう(オペランドは整数である必要があります)。また、math.hのpow関数を使った累乗のヒントも追加してみてください。
総合知識の復習
これら3つのプロジェクトは最初の9回の核心的な知識をカバーしています。
| プロジェクト | カバーする概念 |
|---|---|
| 温度変換 | 変数と型、算術演算、printf書式、scanf入力、整数除算の落とし穴 |
| 閏年判定 | 剰余、関係演算子、論理演算子、if-else、三項演算子、入力検証 |
| シンプルな電卓 | switch-case、do-whileループ、continue、バッファ処理、ゼロ除算チェック |
良いプログラムを書く一般的な手順
- 要件を理解する:入力は何か?出力は何か?エッジケースは何か?
- 構造を選ぶ:どの文を使うか?ifかswitchか?forかwhileか?
- 擬似コードを書く:自然言語で論理を記述してからCに翻訳する
- 段階的に実装する:書きながらテストする。すべて書いてからデバッグしない
- 徹底的にテストする:正常値、境界値、無効値をテストする
❓ よくある質問
Q なぜ
scanfではdoubleに%lfが必要なのに、printfでは%fを使うのですか?A
scanfはメモリに正しく書き込むために仮引数の正確なサイズを知る必要があり、doubleとfloatはサイズが異なります。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デバッグ」と呼ばれ、最も基本的で効果的なデバッグ手法です。📖 まとめ
- 完全なプログラムを書くことがCをマスターする最も早い道です。読むだけでは到達できません
- 整数除算は浮動小数点演算で最もよくある落とし穴です。少なくとも1つのオペランドを小数として書きましょう
- 複雑な条件は段階的に導出しましょう。基本ルールを先に書き、修正を追加していきます
- switch-caseとdo-whileの組み合わせは対話型メニューの古典的なパターンです
- 一般的なプログラミングの流れ:要件理解 → 構造選択 → 擬似コード → 実装 → テスト
📝 練習問題
- 温度プログラムに逆変換機能を追加してください。1で摂氏→華氏、2で華氏→摂氏を選択し、switchまたはif-elseを使います。
- 閏年プログラムを拡張し、開始年と終了年を受け付けて、その範囲のすべての閏年を出力するようにしてください(forループを使用)。
- 電卓に剰余
%と整数除算を追加してください。剰余には整数オペランドが必要なため、ユーザーに整数の入力を促すようにします。



