第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 実践:成績判定プログラム

    仕様

  • 点数を入力として受け取る
  • 90点以上: A
  • 80点以上: B
  • 70点以上: C
  • 60点以上: D
  • 60点未満: F
  • 範囲外(0未満または100超過)はエラー

実装

#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文
- ifelse ifelse - ネスト(入れ子)

  • switch文
- 整数値による多分岐 - break の重要性

  • 三項演算子
- 条件 ? 真 : 偽 - シンプルな2択に使用

よくあるバグ

  • === の混同
  • break の書き忘れ
  • 範囲チェックの境界ミス(<<= の使い分け)
  • 複雑な条件式の優先順位
  • 次の章の予告

    次章では、ループ(繰り返し)を学びます。同じ処理を何度も実行する方法を覚えましょう。

    ---

    Column: 条件分岐の歴史

    最初の条件分岐

    コンピュータの黎明期、プログラムは「パンチカード」で書かれていました。

    条件分岐は「条件付きジャンプ」という命令で実現されていました。条件が真なら別のカードに飛ぶ、という単純な仕組みです。

    現代のプログラミング言語では、ifswitch といった分かりやすい構文が使えますが、コンピュータの内部では今でも「条件付きジャンプ」命令が実行されています。

    構造化プログラミング

    1960年代後半、エドガー・ダイクストラは「GOTO文は有害だ」という有名な論文を発表しました。

    当時のプログラムは GOTO 文で自由に飛び回る「スパゲッティコード」が横行していました。これは保守が困難で、バグの温床でした。

    ダイクストラは、以下の3つの構造だけでプログラムを書くべきだと主張しました:

  • 順次(Sequence): 上から下へ順番に実行
  • 選択(Selection): if-else による分岐
  • 反復(Iteration): ループ

この「構造化プログラミング」は、現代のすべてのプログラミング言語の基礎となっています。

今、あなたが学んでいる 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");
}

解答

= は代入演算子。==(比較演算子)を使うべき。 このコードでは x5 が代入され、その結果 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

三項演算子を使って、ab のうち小さい方を min に代入するコードを書いてください。

解答

min = (a < b) ? a : b;

問題5

! 演算子を使って、変数 flag0(偽)のときに "False" と表示するコードを書いてください。

解答

if (!flag) {
    printf("False\n");
}

または

if (flag == 0) {
    printf("False\n");
}

---

次の章では、ループ(繰り返し)を学びます。同じ処理を効率よく実行しましょう!