第5章:判断と分岐
この章で学ぶこと
- 比較演算子
- 論理演算子
- if文による条件分岐
- else if と else
- switch文
- 三項演算子
---
5.1 条件分岐とは
プログラムに判断させる
これまでのプログラムは、上から下へ順番に実行されるだけでした。しかし実際のプログラムでは、状況に応じて異なる処理をする必要があります。
例えば:
- 年齢が18歳以上なら「成人」、未満なら「未成年」と表示
- パスワードが正しければログイン、間違っていればエラー
- 点数が60点以上なら合格、未満なら不合格
このような「もし〜なら」という判断を、条件分岐と呼びます。
条件分岐の基本
if (条件) {
// 条件が真(true)のときに実行
}
条件が真(true)のときだけ、波括弧の中の処理が実行されます。
---
5.2 比較演算子
2つの値を比較する
比較演算子は、2つの値を比較して、結果を真(true)または偽(false)で返します。
| 演算子 | 意味 | 例 |
|--------|------|-----|
| == | 等しい | a == b |
| != | 等しくない | a != b |
| < | より小さい | a < b |
| > | より大きい | a > b |
| <= | 以下 | a <= b |
| >= | 以上 | a >= b |
使用例
int a = 10;
int b = 5;
a == b // 10 == 5 → false (0)
a != b // 10 != 5 → true (1)
a > b // 10 > 5 → true (1)
a < b // 10 < 5 → false (0)
a >= 10 // 10 >= 10 → true (1)
a <= 9 // 10 <= 9 → false (0)
真と偽の値
C言語では:
- 偽(false):
0 - 真(true):
0以外の値(通常は1)
#include <stdio.h>
int main(void)
{
printf("10 == 10: %d\n", 10 == 10); // 1
printf("10 == 5: %d\n", 10 == 5); // 0
printf("10 > 5: %d\n", 10 > 5); // 1
printf("10 < 5: %d\n", 10 < 5); // 0
return 0;
}
==と=の違い
最も多いバグの1つです。
// 比較(正しい)
if (x == 10) {
printf("x is 10\n");
}
// 代入(バグ!)
if (x = 10) {
printf("This always runs!\n");
}
x = 10 は代入であり、その結果は 10(真)です。条件は常に真になってしまいます。
対策: 10 == x と書く習慣をつける人もいます。この場合、誤って 10 = x と書くとコンパイルエラーになります。
---
5.3 論理演算子
複数の条件を組み合わせる
論理演算子を使うと、複数の条件を組み合わせられます。
| 演算子 | 意味 | 例 |
|--------|------|-----|
| && | AND(かつ) | a && b |
| || | OR(または) | a || b |
| ! | NOT(否定) | !a |
AND演算子(&&)
両方の条件が真のときのみ、結果が真になります。
int age = 25;
int income = 500;
// 年齢が20以上 かつ 収入が300以上
if (age >= 20 && income >= 300) {
printf("条件を満たしています\n");
}
真理値表: | A | B | A && B | |---|---|--------| | 真 | 真 | 真 | | 真 | 偽 | 偽 | | 偽 | 真 | 偽 | | 偽 | 偽 | 偽 |
OR演算子(||)
どちらかの条件が真なら、結果が真になります。
int day = 6; // 土曜日
// 土曜日 または 日曜日
if (day == 6 || day == 7) {
printf("週末です\n");
}
真理値表: | A | B | A \|\| B | |---|---|----------| | 真 | 真 | 真 | | 真 | 偽 | 真 | | 偽 | 真 | 真 | | 偽 | 偽 | 偽 |
NOT演算子(!)
条件を反転させます。
int is_valid = 0; // false
if (!is_valid) {
printf("無効です\n");
}
| A | !A | |---|-----| | 真 | 偽 | | 偽 | 真 |
短絡評価
&& と || には短絡評価(ショートサーキット)という特性があります。
// &&の場合:左が偽なら、右は評価されない
if (0 && func()) { // func()は呼ばれない
// ...
}
// ||の場合:左が真なら、右は評価されない
if (1 || func()) { // func()は呼ばれない
// ...
}
これを利用したテクニック:
// 配列の範囲チェックと要素アクセスを1行で
if (index >= 0 && index < size && array[index] != 0) {
// 安全にアクセス
}
---
5.4 if文
基本形
if (条件) {
// 条件が真のときに実行する処理
}
例:
int age = 20;
if (age >= 18) {
printf("You are an adult.\n");
}
if-else文
条件が偽のときの処理も書けます。
if (条件) {
// 条件が真のときに実行
} else {
// 条件が偽のときに実行
}
例:
int age = 15;
if (age >= 18) {
printf("You are an adult.\n");
} else {
printf("You are a minor.\n");
}
出力:
You are a minor.
if-else if-else文
複数の条件を順番にチェックします。
if (条件1) {
// 条件1が真のとき
} else if (条件2) {
// 条件1が偽で、条件2が真のとき
} else if (条件3) {
// 条件1,2が偽で、条件3が真のとき
} else {
// すべての条件が偽のとき
}
例:成績判定
int score = 75;
if (score >= 90) {
printf("A\n");
} else if (score >= 80) {
printf("B\n");
} else if (score >= 70) {
printf("C\n");
} else if (score >= 60) {
printf("D\n");
} else {
printf("F\n");
}
出力:
C
ネスト(入れ子)
if文の中にif文を入れることもできます。
int age = 25;
int has_license = 1;
if (age >= 18) {
if (has_license) {
printf("運転できます\n");
} else {
printf("免許を取得してください\n");
}
} else {
printf("18歳になってから免許を取得してください\n");
}
深いネストは読みにくくなるので、&& を使って平坦にすることも検討しましょう。
if (age >= 18 && has_license) {
printf("運転できます\n");
} else if (age >= 18) {
printf("免許を取得してください\n");
} else {
printf("18歳になってから免許を取得してください\n");
}
波括弧の省略
処理が1文だけの場合、波括弧を省略できます。
if (x > 0)
printf("Positive\n");
else
printf("Not positive\n");
ただし、常に波括弧を書くことをお勧めします。後から行を追加するときにバグの原因になりやすいからです。
// バグの例
if (x > 0)
printf("Positive\n");
printf("x is %d\n", x); // これは常に実行される!
---
5.5 switch文
複数の値で分岐
1つの変数の値によって、多くの分岐がある場合は switch 文が便利です。
switch (式) {
case 値1:
// 式が値1のとき
break;
case 値2:
// 式が値2のとき
break;
default:
// どれにも該当しないとき
break;
}
例:曜日の判定
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
case 4:
printf("Thursday\n");
break;
case 5:
printf("Friday\n");
break;
case 6:
printf("Saturday\n");
break;
case 7:
printf("Sunday\n");
break;
default:
printf("Invalid day\n");
break;
}
出力:
Wednesday
breakの重要性
break がないと、次の case に「落ちていきます」(フォールスルー)。
int num = 1;
switch (num) {
case 1:
printf("One\n");
// breakがない!
case 2:
printf("Two\n");
break;
case 3:
printf("Three\n");
break;
}
出力:
One
Two
意図的にフォールスルーを使うこともあります:
int month = 2;
int days;
switch (month) {
case 1: case 3: case 5: case 7: case 8: case 10: case 12:
days = 31;
break;
case 4: case 6: case 9: case 11:
days = 30;
break;
case 2:
days = 28; // うるう年は考慮しない
break;
default:
days = -1;
break;
}
switchの制限
- 整数型(int, char など)でしか使えない
- float, double, 文字列では使えない
- 範囲での指定はできない(
case 1..10:は不可)
---
5.6 三項演算子
簡潔な条件分岐
単純な条件分岐は、三項演算子(条件演算子)で1行に書けます。
変数 = 条件 ? 真のときの値 : 偽のときの値;
例:
int a = 10;
int b = 20;
int max;
// if-else で書く場合
if (a > b) {
max = a;
} else {
max = b;
}
// 三項演算子で書く場合
max = (a > b) ? a : b;
使用例
int score = 75;
char *result = (score >= 60) ? "Pass" : "Fail";
printf("%s\n", result);
int age = 17;
printf("%s\n", (age >= 18) ? "Adult" : "Minor");
注意点
複雑な条件には使わないこと。読みにくくなります。
// 悪い例:読みにくい
int result = (a > 0) ? ((b > 0) ? a + b : a - b) : ((b > 0) ? b - a : -(a + b));
単純な2択の場合にのみ使用しましょう。
---
5.7 実践:成績判定プログラム
仕様
実装
#include <stdio.h>
int main(void)
{
int score;
char grade;
printf("Enter your score (0-100): ");
scanf("%d", &score);
if (score < 0 || score > 100) {
printf("Error: Score must be between 0 and 100.\n");
return 1;
}
if (score >= 90) {
grade = 'A';
} else if (score >= 80) {
grade = 'B';
} else if (score >= 70) {
grade = 'C';
} else if (score >= 60) {
grade = 'D';
} else {
grade = 'F';
}
printf("Score: %d, Grade: %c\n", score, grade);
if (grade == 'A' || grade == 'B') {
printf("Excellent!\n");
} else if (grade == 'C' || grade == 'D') {
printf("Keep working hard!\n");
} else {
printf("You need to study more.\n");
}
return 0;
}
実行例:
Enter your score (0-100): 85
Score: 85, Grade: B
Excellent!
Enter your score (0-100): 45
Score: 45, Grade: F
You need to study more.
---
5.8 実践:うるう年判定
うるう年の条件
- 4で割り切れる年はうるう年
- ただし、100で割り切れる年はうるう年ではない
- ただし、400で割り切れる年はうるう年
実装
#include <stdio.h>
int main(void)
{
int year;
printf("Enter a year: ");
scanf("%d", &year);
if ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)) {
printf("%d is a leap year.\n", year);
} else {
printf("%d is not a leap year.\n", year);
}
return 0;
}
実行例:
Enter a year: 2024
2024 is a leap year.
Enter a year: 1900
1900 is not a leap year.
Enter a year: 2000
2000 is a leap year.
---
5.9 この章のまとめ
学んだこと
- 比較演算子
==, !=, <, >, <=, >=
- == と = を間違えないこと- 論理演算子
&&(AND)、||(OR)、!(NOT)
- 短絡評価- if文
if、else if、else
- ネスト(入れ子)- switch文
break の重要性- 三項演算子
条件 ? 真 : 偽
- シンプルな2択に使用よくあるバグ
==と=の混同breakの書き忘れ- 範囲チェックの境界ミス(
<と<=の使い分け) - 複雑な条件式の優先順位
- 順次(Sequence): 上から下へ順番に実行
- 選択(Selection): if-else による分岐
- 反復(Iteration): ループ
次の章の予告
次章では、ループ(繰り返し)を学びます。同じ処理を何度も実行する方法を覚えましょう。
---
Column: 条件分岐の歴史
最初の条件分岐
コンピュータの黎明期、プログラムは「パンチカード」で書かれていました。
条件分岐は「条件付きジャンプ」という命令で実現されていました。条件が真なら別のカードに飛ぶ、という単純な仕組みです。
現代のプログラミング言語では、if や switch といった分かりやすい構文が使えますが、コンピュータの内部では今でも「条件付きジャンプ」命令が実行されています。
構造化プログラミング
1960年代後半、エドガー・ダイクストラは「GOTO文は有害だ」という有名な論文を発表しました。
当時のプログラムは GOTO 文で自由に飛び回る「スパゲッティコード」が横行していました。これは保守が困難で、バグの温床でした。
ダイクストラは、以下の3つの構造だけでプログラムを書くべきだと主張しました:
この「構造化プログラミング」は、現代のすべてのプログラミング言語の基礎となっています。
今、あなたが学んでいる if 文や次の章で学ぶ for 文は、50年以上の歴史を持つ、プログラミングの根幹をなす概念なのです。
---
確認問題
問題1
以下のコードの出力は何ですか?int x = 5;
if (x > 3 && x < 10) {
printf("A\n");
} else {
printf("B\n");
}
解答
A
5 > 3 は真、5 < 10 も真。両方真なので、&& の結果も真。
問題2
以下のコードの問題点は何ですか?int x = 10;
if (x = 5) {
printf("x is 5\n");
}
解答
= は代入演算子。==(比較演算子)を使うべき。
このコードでは x に 5 が代入され、その結果 5(真)なので、常に "x is 5" が表示される。
正しいコード:
if (x == 5) {
問題3
以下のswitch文で、num が 2 のとき、何が出力されますか?int num = 2;
switch (num) {
case 1:
printf("One\n");
case 2:
printf("Two\n");
case 3:
printf("Three\n");
break;
default:
printf("Other\n");
}
解答
Two
Three
case 2: の後に break がないため、case 3: もフォールスルーで実行される。
問題4
三項演算子を使って、a と b のうち小さい方を min に代入するコードを書いてください。解答
min = (a < b) ? a : b;
問題5
! 演算子を使って、変数 flag が 0(偽)のときに "False" と表示するコードを書いてください。解答
if (!flag) {
printf("False\n");
}
または
if (flag == 0) {
printf("False\n");
}
---
次の章では、ループ(繰り返し)を学びます。同じ処理を効率よく実行しましょう!