C言語の基本|switch文の注意点

switch文は便利だからこそ、型・case・breakを正しく扱おう。落とし穴を避けて、読みやすい分岐を作るのがポイントです。

switch文の注意点

switch文は、入力された値に応じて処理を切り替えたいときに便利な制御文です。caseを一覧のように並べられるため、選択肢が増えても処理を見つけやすくなります。

ただし、switch文はどのような条件にも使えるわけではありません。

  • switchの式に使用できる型が決まっている
  • caseには固定された値を書く必要がある
  • 範囲や複雑な条件を直接指定できない
  • 複数の値を1つのcaseに並べられない
  • breakを書き忘れると次のcaseまで実行される

これらのルールを知らずに使うと、コンパイルエラーになったり、想定していない処理が実行されたりします。

switch文が向いている条件とif文が向いている条件を見分けながら、安全な書き方を確認していきましょう。

図1:switch文で使える条件と使えない条件

GPT-image-2用プロンプト:

この図から分かること

switch文は、整数や文字などの値が、指定したcaseの値と一致するかを調べることが得意です。

一方、小数、文字列、範囲、大小比較、複数条件の組み合わせには向いていません。switch文で書きにくい条件を無理に変換するのではなく、if文と使い分けることが大切です。

switchの式に使用できる型

switchの丸括弧内に書く式は、結果が整数型または列挙型になる必要があります。

switch (式) {
    /* caseを記述する */
}

代表的な型を整理すると、次のようになります。

型やデータswitchでの使用補足
int使用できるメニュー番号などに向いている
char使用できる1文字による分岐に使える
short使用できる整数型なので使用できる
long使用できる整数型なので使用できる
enum使用できる列挙された値の分岐に向いている
float使用できない浮動小数点型は扱えない
double使用できない浮動小数点型は扱えない
文字列使用できない整数型や列挙型ではない

たとえば、次のように整数型のmodeを指定する書き方は問題ありません。

switch (mode)

一方、double型のtemperatureを直接指定することはできません。

switch (temperature)

温度が20.0以上30.0未満といった小数の範囲を調べる場合は、関係演算子を使えるif文のほうが自然です。

switch文は特定の値との一致に使う

switch文は、式の結果とcaseに書かれた値が一致するかを調べます。

case 1:

このcaseは、switchの式の結果が1だった場合に選ばれます。

switch文が得意なのは、次のような条件です。

  • 値が1なら処理A
  • 値が2なら処理B
  • 値が3なら処理C
  • どの値にも一致しなければdefault

反対に、次のような条件はif文が向いています。

  • 値が0より大きい
  • 値が0以上100以下
  • 温度が20.0以上30.0未満
  • 条件Aと条件Bの両方を満たす
  • 条件Aまたは条件Bを満たす
やりたい判定向いている構文
特定の整数と一致するかswitch
特定の文字と一致するかswitch
大小を比較するif
範囲内か調べるif
&&や||で条件を組み合わせるif
小数を比較するif

caseには整数定数式を書く

caseの右側には、コンパイル時に値が決まる整数定数式を記述します。

caseの例使用できるか理由
case 1:使用できる整数定数である
case 'A':使用できる文字定数である
case 5 + 5:使用できるコンパイル時に10と計算できる
case x:通常は使用できない変数の値は実行時に決まる
case 2.5:使用できない浮動小数点数である
case scanf(...):使用できない関数の結果は定数式ではない

同じswitch文の中に、同じ値のcaseを複数記述することもできません。

case 1:
    printf("処理A\n");
    break;

case 1:
    printf("処理B\n");
    break;

この例ではcase 1が重複しているため、コンパイルエラーになります。

複数の値を1つのcaseには書けない

複数の値で同じ処理を実行したい場合でも、次のようには書けません。

case 1, 2, 3:
    printf("同じ処理です。\n");
    break;

C言語のcaseラベルには、1つの整数定数式を指定します。複数の値をまとめたい場合は、caseを縦に並べます。

case 1:
case 2:
case 3:
    printf("同じ処理です。\n");
    break;

この書き方では、式の結果が1、2、3のどれであっても、同じprintfへ到達します。

caseを並べて処理を共有する仕組み

複数のcaseをまとめる場合は、共有する処理までbreakを書きません。

case 1:
case 2:
case 3:
    共通の処理;
    break;

case 1が選ばれた場合、case 1の直後には処理もbreakもないため、そのままcase 2、case 3を通過して共通処理へ進みます。

case 1の直後にbreakを書くと、そこでswitch文を抜けてしまいます。

case 1:
    break;

case 2:
case 3:
    共通の処理;
    break;

この書き方では、値が1の場合に共通処理が実行されません。複数のcaseを合流させたいときは、最後のcaseの処理後にbreakを置きます。

図2:複数のcaseが共通処理へ合流する流れ

GPT-image-2用プロンプト:

この図から分かること

複数のcaseで同じ処理を行う場合は、それぞれのcaseを共通処理の上に並べます。

途中にbreakを置かないことで、どのcaseから入っても共通処理へ進めます。そして、共通処理が終わった場所にbreakを置き、switch文から抜けます。

caseの順番は自由でも読みやすさを意識する

caseは、必ず小さい値から大きい値へ並べなければならないわけではありません。式の値と一致するcaseが選ばれるため、順番が前後していても分岐できます。

case 3:
    処理3;
    break;

case 1:
    処理1;
    break;

case 2:
    処理2;
    break;

この並びでも動作しますが、caseを探しにくくなります。

caseの並べ方動作読みやすさ
値をばらばらに並べる分岐できる目的のcaseを探しにくい
昇順に並べる分岐できる値の流れを追いやすい
降順に並べる分岐できる規則が明確なら読みやすい
同じ処理ごとにまとめる分岐できる処理の意味を把握しやすい

通常は昇順や降順で揃えると読みやすくなります。ただし、複数のcaseで処理を共有するときは、同じ役割のcaseを近くにまとめることも大切です。

なお、breakがない場合は、選ばれたcaseより下にある処理が続けて実行されます。そのため、フォールスルーを使用するコードではcaseの順番が実行結果にも影響します。

breakを書き忘れるとフォールスルーが起こる

switch文で特に注意したいのが、breakの書き忘れです。

switch (mode) {
case 1:
    printf("自動運転モードです。\n");

case 2:
    printf("手動運転モードです。\n");
    break;
}

modeが1の場合、case 1のprintfを実行します。しかし、その後にbreakがないため、case 2のprintfも続けて実行されます。

自動運転モードです。
手動運転モードです。

このように、一致したcaseの後にある処理まで実行される動きをフォールスルーと呼びます。

基本的には、1つのcaseの処理が終わる場所にbreakを置きます。複数のcaseで処理を共有するなど、目的が明確な場合にだけフォールスルーを利用しましょう。

月番号から四半期を表示する

次のプログラムでは、入力された月を第1四半期から第4四半期までの4つに分類します。

表示する四半期
1、2、3第1四半期
4、5、6第2四半期
7、8、9第3四半期
10、11、12第4四半期
その他入力エラー
複数のcaseをまとめて四半期を判定するプログラム

ファイル名:8_9_1.c

#include <stdio.h>

int main(void)
{
    int month;

    printf("月を1~12の整数で入力してください > ");
    scanf("%d", &month);

    switch (month) {
    case 1:
    case 2:
    case 3:
        printf("第1四半期です。\n");
        break;

    case 4:
    case 5:
    case 6:
        printf("第2四半期です。\n");
        break;

    case 7:
    case 8:
    case 9:
        printf("第3四半期です。\n");
        break;

    case 10:
    case 11:
    case 12:
        printf("第4四半期です。\n");
        break;

    default:
        printf("入力エラー:1~12の整数で入力してください。\n");
        break;
    }

    return 0;
}
実行結果の例

8を入力すると、case 8から第3四半期の共通処理へ進みます。

月を1~12の整数で入力してください > 8
第3四半期です。

有効な月ではない13を入力すると、defaultが実行されます。

月を1~12の整数で入力してください > 13
入力エラー:1~12の整数で入力してください。
プログラムの詳しい解説

8を入力すると、monthには8が格納されます。

switch (month)

switch文はmonthと一致するcase 8から処理を開始します。

case 7:
case 8:
case 9:
    printf("第3四半期です。\n");
    break;

case 8の直後には実行する文がないため、case 9を通過してprintfへ進みます。メッセージを表示した後、breakでswitch文を抜けます。

同じ処理をするcase 7、case 8、case 9をまとめていますが、1つのcaseに複数の値を書いているわけではありません。それぞれ独立したcaseラベルを、共通処理の前に並べています。

13はどのcaseにも一致しないため、defaultの入力エラーが実行されます。

defaultで想定外の値を受け止める

defaultは、どのcaseにも一致しなかったときの処理です。

default:
    printf("入力エラーです。\n");
    break;

defaultは省略できますが、利用者から番号を入力してもらうプログラムでは、想定外の値が入力される可能性があります。

defaultを用意すると、次のような対応ができます。

  • 入力できる番号を案内する
  • 対応していない値であることを知らせる
  • 初期値や異常値を検出する
  • 何も表示されない状態を防ぐ

defaultを最後に書いた場合、末尾のbreakを省略しても次のcaseへ進むことはありません。それでも、後からcaseを追加するときの安全性や書き方の統一を考えて、breakを付けておくと分かりやすくなります。

設備の運転モードを番号で切り替える

次のプログラムでは、入力された番号に応じて設備の運転モードを表示します。

入力番号運転モード
1自動運転
2手動運転
3点検運転
その他入力エラー
番号に一致する設備の運転モードを表示するプログラム

ファイル名:8_9_2.c

#include <stdio.h>

int main(void)
{
    int mode;

    printf("運転モードを選択してください。\n");
    printf("1:自動運転  2:手動運転  3:点検運転 > ");
    scanf("%d", &mode);

    switch (mode) {
    case 1:
        printf("自動運転モードを開始します。\n");
        break;

    case 2:
        printf("手動運転モードを開始します。\n");
        break;

    case 3:
        printf("点検運転モードを開始します。\n");
        break;

    default:
        printf("正しい運転モード番号を入力してください。\n");
        break;
    }

    return 0;
}
実行結果の例

2を入力した場合は、case 2が実行されます。

運転モードを選択してください。
1:自動運転  2:手動運転  3:点検運転 > 2
手動運転モードを開始します。

0を入力した場合は、どのcaseにも一致しないためdefaultが実行されます。

運転モードを選択してください。
1:自動運転  2:手動運転  3:点検運転 > 0
正しい運転モード番号を入力してください。
プログラムの詳しい解説

このプログラムの条件は、modeが1、2、3のどれと一致するかという単純な値の比較です。そのため、switch文が適しています。

modeが2の場合は、case 2から処理を始めます。

case 2:
    printf("手動運転モードを開始します。\n");
    break;

printfの後にbreakがあるため、case 3やdefaultは実行されません。

各caseの末尾にbreakがあることを確認すると、フォールスルーによる意図しない連続実行を防げます。

図3:breakの書き忘れによるフォールスルー

この図から分かること

breakが正しく書かれていれば、選択されたcaseの処理だけを実行してswitch文を抜けます。

breakを書き忘れると、次のcaseラベルでは処理が止まらず、その下の文も続けて実行されます。defaultが下にあれば、defaultの処理まで実行される可能性があります。

switch文とif文を判断する基準

switch文を使うか迷ったときは、条件が値との一致なのか、それとも範囲や論理式なのかを確認しましょう。

条件の内容適した構文
modeが1、2、3のどれかswitch
commandが特定の1文字かswitch
temperatureが30以上かif
scoreが0以上100以下かif
条件Aと条件Bの両方を満たすかif
小数を比較するif
文字列の内容を比較するifと文字列比較処理

特定の値を列挙できるならswitch文を使うと、caseが一覧になって読みやすくなります。

範囲や複数条件を無理に番号へ置き換えると、かえって処理が分かりにくくなります。switch文の制限に当てはまるときは、if文を選ぶことも大切です。