コンテンツにスキップ

C言語/switch

出典: フリー教科書『ウィキブックス(Wikibooks)』

はじめに

[編集]

プログラミングにおいて条件分岐は基本的な制御構造の一つです。C言語ではif-else文に加えて、値に基づいて複数の選択肢から一つを選ぶswitch文が提供されています。本章ではswitch文の構文、使用方法、およびプログラミングにおける効果的な活用法について解説します。

switch文の基本構文

[編集]

switch文は、一つの式の値に基づいて複数の実行経路から一つを選択する制御構造です。基本的な構文は以下の通りです:

switch () {
    case 定数1:
        /* 定数1に一致した場合の処理 */
        break;
    case 定数2:
        /* 定数2に一致した場合の処理 */
        break;
    /* 他のcaseラベル */
    default:
        /* どのcaseにも一致しなかった場合の処理 */
}

switch文は次のように動作します:

  1. 最初に式が評価されます
  2. 評価された値に一致するcaseラベルを探します
  3. 一致したcaseラベル以降の文を実行します
  4. break文に達するとswitch文全体を抜けます
  5. どのcaseにも一致しない場合はdefaultラベル以降の文を実行します

簡単な例

[編集]
#include <stdio.h>
int main() {
    int day = 3;
    
    switch (day) {
        case 1:
            printf("月曜日\n");
            break;
        case 2:
            printf("火曜日\n");
            break;
        case 3:
            printf("水曜日\n");
            break;
        case 4:
            printf("木曜日\n");
            break;
        case 5:
            printf("金曜日\n");
            break;
        case 6:
            printf("土曜日\n");
            break;
        case 7:
            printf("日曜日\n");
            break;
        default:
            printf("無効な日付\n");
    }
    
    return 0;
}

この例では、変数dayの値に応じて対応する曜日が表示されます。dayが3であるため、「水曜日」が出力されます。

switch文の重要な特徴

[編集]

1. 式の型制限

[編集]

switch文の式は整数型(charshortintlong)または列挙型でなければなりません。浮動小数点型(floatdouble)や文字列型は使用できません。

使用可能な型 使用不可能な型
char float
short double
int 文字列(char *
long 構造体・共用体
列挙型(enum ポインタ

2. caseラベルの制約

[編集]

caseラベルには定数式のみが使用できます。変数や関数呼び出しは使用できません。

const int MONDAY = 1;

switch (day) {
    case MONDAY:        /* 正しい: 定数 */
    case 2:             /* 正しい: リテラル */
    case MONDAY + 2:    /* 正しい: 定数式 */
    /* case day:        /* 誤り: 変数は使用不可 */
    /* case getValue(): /* 誤り: 関数呼び出しは使用不可 */
}

3. フォールスルー(Fall-through)

[編集]

switch文における重要な特徴の一つは「フォールスルー」と呼ばれる動作です。break文がない場合、一致したcase以降のすべての文が実行されます。

switch (value) {
    case 1:
        printf("1以上\n");
        /* breakがないので次のcaseも実行される */
    case 2:
        printf("2以下\n");
        break;
    case 3:
        printf("3です\n");
        break;
}

valueが1の場合、「1以上」と「2以下」の両方が表示されます。valueが2の場合は「2以下」のみが表示されます。

4. defaultラベル

[編集]

defaultラベルはどのcaseにも一致しない場合に実行されます。defaultラベルは省略可能ですが、予期せぬ入力に対処するために記述することが推奨されます。

switch (grade) {
    case 'A':
        printf("優秀\n");
        break;
    case 'B':
        printf("良好\n");
        break;
    case 'C':
        printf("普通\n");
        break;
    default:
        printf("無効な成績\n");
}

switch文の活用例

[編集]

複数の値を一つの処理にまとめる

[編集]

フォールスルーを利用して、複数の値に対して同じ処理を行うことができます。

switch (day) {
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
        printf("平日です\n");
        break;
    case 6:
    case 7:
        printf("週末です\n");
        break;
    default:
        printf("無効な日付\n");
}

メニュー選択の実装

[編集]
#include <stdio.h>
int main() {
    int choice;
    
    printf("メニュー:\n");
    printf("1. ファイルを開く\n");
    printf("2. ファイルを保存\n");
    printf("3. 設定\n");
    printf("4. 終了\n");
    printf("選択してください: ");
    scanf("%d", &choice);
    
    switch (choice) {
        case 1:
            printf("ファイルを開きます...\n");
            break;
        case 2:
            printf("ファイルを保存します...\n");
            break;
        case 3:
            printf("設定画面を表示します...\n");
            break;
        case 4:
            printf("プログラムを終了します\n");
            break;
        default:
            printf("無効な選択です\n");
    }
    
    return 0;
}

状態マシンの実装

[編集]

switch文は状態マシンの実装に適しています。

enum State { INIT, READY, RUNNING, PAUSED, STOPPED };

void process_state(enum State current_state) {
    switch (current_state) {
        case INIT:
            initialize_system();
            break;
        case READY:
            prepare_execution();
            break;
        case RUNNING:
            execute_tasks();
            break;
        case PAUSED:
            wait_for_resume();
            break;
        case STOPPED:
            cleanup_resources();
            break;
    }
}

switch文とif-else文の比較

[編集]
特性 switch if-else
条件式の型 整数型・列挙型のみ あらゆる型の式
比較方法 等値比較のみ あらゆる比較演算子
コード構造 多数の選択肢に適した構造 複雑な条件に適した構造
実行速度 多くの場合、最適化されたジャンプテーブル 条件の数に応じて増加
フォールスルー 明示的なbreakが必要 なし

switch文使用時の注意点

[編集]

1. break文の忘れ

[編集]

switch文を使用する際の最も一般的なバグはbreak文の忘れです。意図しないフォールスルーによって予期せぬ動作を引き起こす可能性があります。

/* 潜在的なバグの例 */
switch (option) {
    case 1:
        perform_action_1();
        /* breakを忘れた! */
    case 2:
        perform_action_2();
        break;
}

optionが1の場合、perform_action_1()perform_action_2()の両方が実行されます。

2. コンパイラの警告

[編集]

多くの現代的なCコンパイラは、意図しないフォールスルーに対して警告を発します。例えばClangやGCCでは-Wimplicit-fallthroughオプションを使用できます。

3. 意図的なフォールスルーの明示

[編集]

意図的にフォールスルーを使用する場合は、コメントでその意図を明確にすることが推奨されます。

switch (option) {
    case 1:
        perform_setup();
        /* 意図的なフォールスルー */
    case 2:
        perform_action();
        break;
}

C23では[[fallthrough]]属性が導入され、意図的なフォールスルーを明示できるようになります。

複雑なswitch文の例

[編集]

以下は、計算機プログラムにおけるswitch文の使用例です:

#include <stdio.h>
int main() {
    double num1, num2, result;
    char operator;
    
    printf("簡易計算機\n");
    printf("式を入力してください(例: 5 + 3): ");
    scanf("%lf %c %lf", &num1, &operator, &num2);
    
    switch (operator) {
        case '+':
            result = num1 + num2;
            printf("%.2lf + %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '-':
            result = num1 - num2;
            printf("%.2lf - %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '*':
            result = num1 * num2;
            printf("%.2lf * %.2lf = %.2lf\n", num1, num2, result);
            break;
        case '/':
            if (num2 != 0) {
                result = num1 / num2;
                printf("%.2lf / %.2lf = %.2lf\n", num1, num2, result);
            } else {
                printf("エラー: ゼロで除算できません\n");
            }
            break;
        default:
            printf("エラー: 無効な演算子です\n");
    }
    
    return 0;
}

まとめ

[編集]

switch文はC言語における条件分岐のための強力な制御構造です。多くの選択肢から一つを選ぶ場合に特に有用で、適切に使用することでコードの可読性と実行効率を向上させることができます。ただし、フォールスルーの動作を理解し、break文の配置に注意することが重要です。

switch文は複雑な条件分岐が必要な場合にはif-else文と組み合わせて使用することも多く、C言語プログラミングにおける基本的なスキルの一つです。適切な場面で適切な制御構造を選択することで、効率的で保守性の高いコードを書くことができるでしょう。