WisdomSoft - for your serial experiences.

3.3 switch文

1つの値から、対応する複数のコードのいずれかを選択して実行する switch 文を紹介します。

3.3.1 多分岐判断

ある値の状況によってプログラムの流れを分岐させるには if 文を用いることで解決できます。分岐の数が多い場合は if 文を入れ子にした if else if else .... という構造を作ることによって実装できますが、これがあまりにも多い場合、if 文はあまり適しておりません。

例えば、プログラムに意味のある数値をメッセージとして渡し、プログラムは受け取った値を解析して適切な処理を施すというシステムを実現したい場合、メッセージの数は膨大になる可能性があります。そこで、このようなプログラムを実現するには switch 文を用いる手段が最適だと考えられます。

switch文
switch () {
case 定数:
	ブロック文
default:
	デフォルト・ブロック文
}

switch 文は、指定した式を評価して case に指定した定数式と一致したブロック文から実行を開始します。case は複数指定することができますが、定数が重複してはいけません。default は全ての case と一致しなかった場合に実行する特殊なラベルです。case で指定することができるのは定数式のみで、変数は指定できないので注意してください。

ところで switch 文は、case や default の後にコロン : をつけるという奇妙な構文を持っていますが、これはラベル定義と呼ばれる構文で、その直後の文に名前をつけるためのものです。ラベルについての詳細は goto 文で解説しますが、case や default もラベルの一種です。

ラベルの概念に基づいて、より正確に switch 文の性質を解説するのであれば、これは分岐というよりジャンプに近い制御文であると考えられます。switch は式を評価して、その結果と同一の定数の case ラベルを持つ文まで移動します。switch を if 文のような分岐文だと思い込んでいる場合、フォールスルーと呼ばれる現象が原因でバグになることがあります。次のような switch 文を考えてください。

switch (0) {
case 0:
	printf("case 0");
case 1:
	printf("case 1");
default:
	printf("default");
}

この switch 文を実行するとどのようになるでしょうか?式の評価部分には 0 という定数を指定しています。これは case 0: ラベルと一致するため、printf("case 0"); が実行されるというところまでは分かりやすいのですが、あろうことかプログラムは printf("case 1"); と printf("default"); までも実行してしまうのです。このように、下部の case や default も実行してしまう switch の性質を一般にフォールスルーと呼びますが、この結果から switch 文の case は if-else とは異なり、単純な文のラベルであることが確認できます。switch 文は式と一致した case ラベルの文にジャンプするだけであるという概念が理解できれば、なぜその後の(他の case や default 以下の)文まで実行してしまうのか納得できるはずです。

ところが、多くの場合フォールスルーは望まれません。できることなら、目的の case ラベルの文を実行した後に switch を抜け出したいと思うことでしょう。これにはいくつかのアプローチがありますが、最も一般的なのは break 文を利用することです。break はこれを含む最も内側の制御を抜け出すという役割があり、switch やループを抜け出すために利用されるキーワードです。次のように記述すれば、特定のラベルを実行した後に抜け出すようになります。

switch (0) {
case 0:
	printf("case 0");
	break;
case 1:
	printf("case 1");
	break;
default:
	printf("default");
	break;
}

因みに、case も default も省略可能であり、指定する位置は任意です。一般的に default はその性質上、一番下(最後)に指定されますが、case ラベルが default をまたがっても問題はありません。default が省略されている状態で一致する case が存在しない場合は何もせずに switch 文を抜け出します。

コード1
#include <stdio.h>

int main() {
	int iSelected;
	printf("あなたは、可愛い猫耳娘の前に立っています\n");
	printf("0=頭をなでる,1=尻尾を触る,2=指を差し出す>");
	scanf("%d" , &iSelected);

	switch(iSelected) {
	case 0:
		printf("喜んでいるようだ\n");
		break;
	case 1:
		printf("ドゲシッ!!猫パンチをくらった\n");
		break;
	case 2:
		printf("指の匂いを嗅いでいる。本能のようだ\n");
		break;
	default:
		printf("正しい選択番号を入力してください\n");
	}
	return 0;
}
実行結果
コード1 実行結果

コード1は簡単な分岐プログラムです。プログラムを実行すると文章が表示され、どのように行動するかを選択するための入力が要求されます。その後、プログラムは入力された値を switch 文で評価して各 case に分岐します。適切な文を表示させた後、プログラムは終了します。

case で指定できるのは定数式でしたが、ASCII コードは 1 バイトの数値で表現できるため、1文字の文字定数を case に指定することもできます。文字定数を指定する場合は case 'A': というように記述します。

コード2
#include <stdio.h>

int main() {
	char chSelected;
	printf("あなたは、可愛い猫耳娘の前に立っています\n");
	printf("A=頭をなでる,B=尻尾を触る,C=指を差し出す>");
	scanf("%c" , &chSelected);

	switch(chSelected) {
	case 'A':
		printf("喜んでいるようだ\n");
		break;
	case 'B':
		printf("ドゲシッ!!猫パンチをくらった\n");
		break;
	case 'C':
		printf("指の匂いを嗅いでいる。本能のようだ\n");
		break;
	default:
		printf("正しい選択記号を入力してください\n");
	}
	return 0;
}
実行結果
コード2 実行結果

このプログラムはコード1を改良して、選択を文字で行うようにしたものです。