条件分岐を読みやすく整理したいときに便利なのがC言語のswitch-case構文です。
特に「1の場合」「2の場合」「それ以外」のように離散的な値で分けたい場面で有効です。
本稿では基本構文からbreakとフォールスルー、defaultの扱い、複数条件のまとめ方、型やスコープの注意点、ループ内での挙動まで、実用的なコツを交えて解説します。
基本構文
switchは整数系の値に応じて分岐する構文
switchは式の評価結果(整数型)に応じて、該当するcaseラベルの位置から処理を開始します。
該当するcaseがないときはdefaultが実行されます。
caseブロックは明示的にbreak
しない限り、次のcaseへ処理が落ちていく(フォールスルー)点がif-elseとの大きな違いです。
基本の書き方
最も基本的な形は次のとおりです。
各caseの末尾でbreak
してswitch文を終了させるのが通常の書き方です。
// 基本構文イメージ(テンプレート)
switch (expr) {
case 1:
// 1に一致したときの処理
break; // switchを抜ける
case 2:
// 2に一致したときの処理
break;
default:
// どのcaseにも当てはまらないとき
break;
}
ポイント
switch
に与えるexpr
は整数型(int
,char
,enum
,long
など)。浮動小数点(float
,double
)は不可です。case
ラベルは「整数定数式」でなければなりません。重複はコンパイルエラーになります。- 通常は各caseで
break
します。省くと次のcaseへ“落ちる”ため、意図しないフォールスルーはバグの温床になります。
「1の場合」「2の場合」「それ以外」の具体例
動くサンプル(複数の入力例を一度に確認)
配列で複数の入力を試し、「1」「2」「それ以外」の出力をまとめて確認します。
#include <stdio.h>
int main(void) {
int xs[] = {1, 2, 5};
size_t n = sizeof(xs) / sizeof(xs[0]);
for (size_t i = 0; i < n; ++i) {
int x = xs[i];
printf("x = %d -> ", x);
switch (x) {
case 1:
puts("1の場合");
break;
case 2:
puts("2の場合");
break;
default:
puts("それ以外");
break;
}
}
return 0;
}
x = 1 -> 1の場合
x = 2 -> 2の場合
x = 5 -> それ以外
breakの役割とフォールスルー
breakはそのswitch文から抜ける
break
は「そのswitch文だけ」を終了させます。
外側にループがあっても、そのループは抜けません。
breakを省くと次のcaseへ処理が落ちる(フォールスルー)
break
を省略すると、該当caseの後に続くcaseの処理も実行されます。
これをフォールスルーと呼びます。
意図する場合もありますが、意図しないフォールスルーはバグの原因になるため注意が必要です。
#include <stdio.h>
int main(void) {
for (int x = 1; x <= 2; ++x) {
printf("x = %d:\n", x);
switch (x) {
case 1:
puts("Aの処理");
/* フォールスルー: 次のcaseへ意図的に落とす */
/* 一部のコンパイラでは以下のようなコメントで警告抑止が可能
// fall through
*/
case 2:
puts("Bの処理");
break;
default:
puts("defaultの処理");
break;
}
}
return 0;
}
x = 1:
Aの処理
Bの処理
x = 2:
Bの処理
ヒント:
- 最近のコンパイラでは
-Wimplicit-fallthrough
等の警告が有効な場合、意図的なフォールスルーに注釈を付けるとよいです(コメントやコンパイラ拡張属性を使用)。
defaultの扱い
defaultは一致するcaseがないときに実行
default
はどのcase
にも一致しなかったときに実行されます。
配置場所は文法上どこでも構いませんが、最後に置くと読みやすくなります。
default
でも処理の最後にbreak
を忘れないようにします。
#include <stdio.h>
int main(void) {
int x = 42;
switch (x) {
case 1:
puts("1");
break;
case 2:
puts("2");
break;
default:
puts("それ以外");
break;
}
return 0;
}
それ以外
複数条件をまとめる書き方
同じ処理はcaseを連続して書く
同じ処理にまとめたいときは、case
を並べてbreak
を一つだけ書きます。
#include <stdio.h>
int main(void) {
int xs[] = {1, 2, 3};
size_t n = sizeof(xs) / sizeof(xs[0]);
for (size_t i = 0; i < n; ++i) {
int x = xs[i];
printf("x = %d -> ", x);
switch (x) {
case 1:
case 2:
puts("1または2");
break;
default:
puts("それ以外");
break;
}
}
return 0;
}
x = 1 -> 1または2
x = 2 -> 1または2
x = 3 -> それ以外
範囲はcaseでは書けないのでifで補う
case 1 ... 5
のように範囲指定は標準Cでは書けません。
必要に応じてif
を併用します。
#include <stdio.h>
int main(void) {
int xs[] = {0, 3, 7};
size_t n = sizeof(xs) / sizeof(xs[0]);
for (size_t i = 0; i < n; ++i) {
int x = xs[i];
printf("x = %d -> ", x);
switch (x) {
case 0:
puts("特別扱い(0)");
break;
default:
if (1 <= x && x <= 5) {
puts("1〜5の範囲");
} else {
puts("それ以外");
}
break;
}
}
return 0;
}
x = 0 -> 特別扱い(0)
x = 3 -> 1〜5の範囲
x = 7 -> それ以外
型と定数のルール
使用できる式とcaseラベル
- 式(
switch (expr)
のexpr
):- 整数型に限られます。
int
,char
,short
,long
,long long
,_Bool
(bool
)やenum
が対象です。 - 浮動小数点型(
float
,double
,long double
)は不可です。
- 整数型に限られます。
case
ラベル:- 「整数定数式」のみ。数値リテラル、
enum
定数、sizeof
や算術からなる定数式など。 - 同じ値の重複は不可です(コンパイルエラー)。
- 「整数定数式」のみ。数値リテラル、
- 型変換の注意:
char
やshort
は整数昇格して比較されます(多くの環境でint
相当)。enum
は可読性を上げ、case
に意味のある名前を与えられます。
enumで可読性を高める例
#include <stdio.h>
typedef enum {
MODE_NONE = 0,
MODE_ADD = 1,
MODE_SUB = 2
} Mode;
int main(void) {
Mode m = MODE_SUB;
switch (m) {
case MODE_ADD:
puts("加算モード");
break;
case MODE_SUB:
puts("減算モード");
break;
case MODE_NONE:
default:
puts("未選択");
break;
}
return 0;
}
減算モード
参考(よくある可否の早見)
- OK:
switch ((int)c) { ... }
,switch (status_enum) { ... }
,case 10+2: ...
- NG:
switch (3.14) { ... }
,case x:
(変数は不可),case 1.5:
(浮動小数は不可)
変数宣言とスコープの注意
case直後に変数を初期化するならブロックで囲む
case
ラベル直後に初期化を伴う宣言を書くと、制御フロー上の到達性やスコープの問題でコンパイルエラーや警告の原因になります。
ブロック{}
で囲んでスコープを限定しましょう。
#include <stdio.h>
int main(void) {
int x = 1;
switch (x) {
case 1: {
int a = 10; // ブロックスコープで安全に初期化
printf("a = %d\n", a);
break;
}
default:
puts("default");
break;
}
return 0;
}
a = 10
補足:
- 変数の寿命や名前の衝突を避けるためにも、ブロックで囲む習慣は有効です。
ループ内でのbreak/continue
switch内のbreakはswitchのみを抜ける
ループの内側にswitch
があるとき、switch
内のbreak
はループではなくswitch
を終了します。
ループ自体を抜けたい場合は、フラグ変数やgoto
など別の手段が必要です。
continueはループに作用する(switchではない)
switch
の中でcontinue
を書くと、それは内側のswitch
ではなく外側のループに作用して、次の反復へ進みます。
#include <stdio.h>
int main(void) {
for (int i = 0; i < 5; ++i) {
switch (i) {
case 2:
puts("hit 2: break -> ループは続行");
break; // switchのみを抜ける
case 3:
puts("hit 3: continue -> ループの次反復へ");
continue; // forループに作用
default:
printf("i = %d\n", i);
break;
}
puts("switchを抜けた後の処理");
}
return 0;
}
i = 0
switchを抜けた後の処理
i = 1
switchを抜けた後の処理
hit 2: break -> ループは続行
switchを抜けた後の処理
hit 3: continue -> ループの次反復へ
i = 4
switchを抜けた後の処理
まとめ
基本は「caseごとに処理を書き、最後にbreak
」「どれにも当てはまらないときのためにdefault
」という型です。
同一処理はcaseを並べて集約し、範囲や複雑な条件はif
を併用して表現します。
switch
の式は整数型のみ、case
は整数定数式のみ。enum
を活用すると可読性が上がります。
break
はswitchのみを抜け、ループ内ではcontinue
がループに作用します。意図しないフォールスルーを避け、必要な場合は明示的なコメント等で意図を残しましょう。
変数宣言はブロックでスコープを限定すると安全です。これらのポイントを押さえることで、「1の場合」「2の場合」「それ以外」といった典型的な分岐を、読みやすく堅牢に書けます。