C言語の基本|switch文の使い方

値が決まれば、進む先もすぐ決まる。switch文で、選択肢が並ぶ条件分岐をすっきり整理しよう。

if~else if~elseを使うと、範囲判定や複数の条件を組み合わせた分岐を作れます。

一方、プログラムでは、入力された値が1なら処理A、2なら処理B、3なら処理Cというように、特定の値との一致によって処理を選びたい場面もあります。

このような分岐をif文で書くと、同じ変数を何度も比較することになります。

if (mode == 1) {
    処理1;
} else if (mode == 2) {
    処理2;
} else if (mode == 3) {
    処理3;
} else {
    その他の処理;
}

値ごとの選択肢を並べたいときに便利なのがswitch文です。switch文では、それぞれの選択肢をcaseとして一覧のように記述できます。

図1:値に対応するcaseを選ぶswitch文

この図から分かること

switch文は、式の結果と各caseの値を照合し、一致した場所から処理を始めます。

たとえば式の結果が2なら、case 2の処理が選ばれます。どのcaseにも一致しなかった場合は、defaultの処理へ進みます。

switch文の基本構文

switch文は、次の形で記述します。

switch (式) {
case 定数式1:
    文1;
    break;

case 定数式2:
    文2;
    break;

default:
    文3;
    break;
}

処理の流れは次のとおりです。

  1. switchの式を評価する
  2. 式の結果と各caseの値を照合する
  3. 一致するcaseがあれば、その場所から処理を始める
  4. breakを実行したらswitch文を抜ける
  5. 一致するcaseがなければdefaultへ進む
  6. defaultもなければswitch文の中では何も実行しない

switch文を構成する要素

要素役割注意点
switch分岐に使用する式を指定する式の結果は整数型または列挙型である必要がある
case一致させる値を指定する右側には整数定数式を書く
caseが選ばれた場合の処理複数の文を続けて書ける
breakswitch文から抜ける忘れると次のcaseの処理へ進む
defaultどのcaseにも一致しない場合の処理省略できるが、想定外の値への対応に役立つ

switchの式で使える型

switchの丸括弧内には、結果が整数型または列挙型になる式を指定します。

使用できる代表例使用できない代表例
intfloat
chardouble
short小数を結果とする式
long文字列
enum構造体

char型も整数型の一種なので、文字による分岐にも使用できます。

switch (command)

ただし、doubleやfloatのような浮動小数点型はswitch文の式には使用できません。小数の範囲を調べたい場合は、if文を使用するのが自然です。

caseには固定された値を書く

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

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

同じswitch文の中に、同じ値のcaseを複数書くこともできません。

case 1:
    処理1;
    break;

case 1:
    処理2;
    break;

この例ではcase 1が重複しているため、どちらを選ぶべきか決められず、コンパイルエラーになります。

caseと値の間には区切りが必要

caseはC言語のキーワードです。値とは別の字句として記述する必要があります。

case 1:

case1:と書くと、caseと1を組み合わせたcaseラベルにはなりません。case1という通常の識別子によるラベルとして解釈される可能性があり、意図したswitch文の分岐にならないため注意しましょう。

caseの後に波括弧は必須ではない

caseはif文のようなブロックではなく、処理を開始する位置を示すラベルです。そのため、通常はcaseごとに波括弧を付けません。

case 1:
    printf("処理1です。\n");
    printf("複数の文を実行できます。\n");
    break;

この例では、case 1が選ばれると2つのprintfを順番に実行し、breakでswitch文を抜けます。

必要があればcaseの中に波括弧を使うこともできますが、基本的なswitch文では、case、処理、breakという並びで覚えると分かりやすいでしょう。

breakでswitch文を抜ける

breakは、現在実行しているswitch文を終了させるために使います。

case 1:
    printf("気温データを表示します。\n");
    break;

case 1の処理が終わった後にbreakが実行されると、プログラムはswitch文の後へ進みます。

breakはswitch文全体を終了させるものであり、プログラムそのものを終了させる命令ではありません。

breakがない場合は次のcaseへ進む

caseの処理にbreakがない場合、次に書かれているcaseの処理も続けて実行されます。この動きをフォールスルーと呼びます。

switch (value) {
case 1:
    printf("case 1です。\n");

case 2:
    printf("case 2です。\n");
    break;
}

valueが1の場合、実行はcase 1から始まります。しかし、case 1にbreakがないため、そのままcase 2の処理まで実行されます。

図2:breakの有無による処理の違い

この図から分かること

breakがある場合は、選ばれたcaseの処理だけを実行してswitch文から抜けます。

breakがない場合は、次のcaseラベルを通過して、その下の処理まで続けて実行します。意図していないフォールスルーは、複数のメッセージが表示されるなどの不具合につながります。

意図的にフォールスルーを使う場合

複数のcaseで同じ処理を行いたいときは、caseを続けて記述できます。

case 1:
case 2:
    printf("1または2が入力されました。\n");
    break;

この場合、値が1でも2でも同じprintfが実行されます。

case 1の直後には実行する文がなく、そのままcase 2へ進むことを利用しています。意図的なフォールスルーは便利ですが、処理の意味が分かりにくくならないように使うことが大切です。

defaultは一致するcaseがない場合を受け持つ

defaultは、switchの式と一致するcaseがなかったときに実行されます。

default:
    printf("対応していない値です。\n");
    break;

defaultは省略できます。省略した状態でどのcaseにも一致しなかった場合、switch文の中では何も実行されません。

利用者が番号を入力するプログラムでは、想定外の値が入力される可能性があります。そのため、入力エラーや再入力の案内を表示するdefaultを用意すると親切です。

defaultを最後に書いた場合、その後にcaseはありません。そのため、最後のbreakを省略しても直後のcaseへ進む問題は起きません。ただし、後からcaseを追加する可能性や書き方の統一を考え、breakを記述しておくと分かりやすくなります。

観測データの種類を番号で選ぶ

次のプログラムでは、入力された番号に応じて、表示する気象観測データを選択します。

入力する番号選択されるデータ
1気温データ
2湿度データ
3風速データ
その他対応していない番号
番号から表示する気象観測データを選ぶプログラム

ファイル名:8_8_1.c

#include <stdio.h>

int main(void)
{
    int sensor;

    printf("表示するデータを選んでください。\n");
    printf("1:気温  2:湿度  3:風速 > ");
    scanf("%d", &sensor);

    switch (sensor) {
    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
湿度データを表示します。

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

表示するデータを選んでください。
1:気温  2:湿度  3:風速 > 9
対応していない番号です。
プログラムの詳しい解説

入力された整数はsensorに格納されます。

scanf("%d", &sensor);

続いて、switchの式としてsensorを指定しています。

switch (sensor)

sensorが2の場合、switch文はcase 2を探します。

case 2:
    printf("湿度データを表示します。\n");
    break;

case 2のprintfを実行した後、breakによってswitch文を抜けます。case 3やdefaultの処理は実行されません。

9を入力した場合、case 1、case 2、case 3のどれにも一致しません。そのため、defaultの処理が実行されます。

if文とswitch文の使い分け

switch文で書ける分岐の多くは、if~else if~elseでも表現できます。ただし、それぞれ得意な条件が異なります。

観点if~else if~elseswitch
得意な判定範囲や複雑な条件特定の値との一致
条件の例score >= 80case 1:
論理演算子&&や||を使用できるcaseには固定された値を書く
浮動小数点条件式で比較できるswitchの式には使用できない
見た目条件式が長くなることがある選択肢を一覧で確認しやすい
主な注意点条件を書く順番breakの書き忘れ

たとえば、点数が80以上かどうかを調べる処理は、範囲判定なのでif文が向いています。

一方、メニュー番号が1、2、3のどれと一致するかを調べる処理は、switch文が向いています。

switch文では範囲を直接指定できない

caseには、特定の値を指定します。そのため、次のような範囲条件をcaseに書くことはできません。

  • 80以上
  • 50以上80未満
  • 0以上100以下

このような条件には、関係演算子を使えるif~else if~elseが適しています。

switch文を使うか迷ったときは、次のように考えると選びやすくなります。

  • 値が特定の番号や文字と一致するかを調べるならswitch
  • 大小関係や範囲を調べるならif
  • 複数の条件を&&や||で組み合わせるならif

scanfで文字を読み取るときの空白

char型の値をswitch文で分岐させることもできます。

文字をscanfの%cで読み取る場合、改行や空白も1文字として読み取られます。直前の入力で残った改行を読み飛ばしたい場合は、%cの前に空白を入れます。

scanf(" %c", &command);

先頭の空白には、改行、スペース、タブなどの空白文字を読み飛ばす働きがあります。

書き方動作
scanf("%c", &command);次の1文字をそのまま読み取る
scanf(" %c", &command);空白文字を読み飛ばしてから1文字を読み取る

int型を%dで読み取る場合、先頭の空白文字は自動的に読み飛ばされます。そのため、今回のsensorの入力では、%dの前に空白を付けなくても問題ありません。

図3:switch文が値を選択して終了するまでの流れ

この図から分かること

switch文では、最初に式の値を確認し、一致するcaseへ処理を移します。一致するcaseがなければdefaultへ進みます。

選ばれた処理の後にbreakを実行すると、各分岐はswitch文の外側へ合流します。この流れを理解しておくと、caseの選択とbreakの役割を一緒に捉えられるようになります。