C言語編 第12章 多方向へ分岐させる

この章の概要

この章の概要です。

else-if

前章で見たように、if文は処理を2方向に分岐させることができます。 else というキーワードを使おうと使うまいと、分岐する方向は2方向にしかなりません。

しかし、「はい」「いいえ」「どちらともいえない」のような質問の答えのように、3方向以上に分岐させたい場面は幾らでもあります。 3方向以上に分岐させる方法は知っておく必要があります。

まずは、これまで通りに if と else という2つのキーワードだけで実現する方法を見てみましょう。

#include <stdio.h>

int main(void)
{
	char in;
	char str[10];

	puts( "質問の答えを入力して下さい。(y/n)" );
	fgets( str, sizeof(str), stdin );
	sscanf( str, "%c", &in );

	if( in == 'y' ){
		puts( "Yes" );
	}
	else if( in == 'n' ){
		puts( "No" );
	}
	else{
		puts( "Others" );
	}

	return 0;
}

実行結果:

質問の答えを入力して下さい。(y/n)
y
Yes

入力された文字が 'y' か 'n' か、それともそれ以外なのかに応じて、3方向に分岐します。

最初に、if文で 'y' との一致を調べます。 一致していなければ、else の方に進みますが、ここには「else if」と書かれています。 「else if」は1つのキーワードという訳ではなく、2つが組み合わさっているだけですから、再び、if文が普通に処理されます。 ここでは 'n' との一致を調べており、一致すれば { } の中へ、一致しなければ最後の else へ進みます。

switch文

多方向への分岐を行うためのもう1つの手段が switch文です。 まずは、else-if のときのサンプルを、switch文で書き変えてみます。

#include <stdio.h>

int main(void)
{
	char in;
	char str[10];

	puts( "質問の答えを入力して下さい。(y/n)" );
	fgets( str, sizeof(str), stdin );
	sscanf( str, "%c", &in );

	switch( in ){
		case 'y':
			puts( "Yes" );
			break;

		case 'n':
			puts( "No" );
			break;

		default:
			puts( "Others" );
			break;
	}

	return 0;
}

実行結果:

質問の答えを入力して下さい。(y/n)
y
Yes

結果はもちろん同じになります。 switch文の構造は次のようになっています。

switch( 整数値 ){
	case 定数整数:
		定数に一致する場合に実行する処理;
		break;
		
	case 定数整数:
		定数に一致する場合に実行する処理;
		break;
		
	default:
		どの定数にも一致しない場合に実行する処理;
		break;
}

switch の直後に来る ( ) の中に整数値を記述し、その後に幾つかの caseラベルが続きます。 ラベルというのは、「○○○:」のような構造で、ソースコード上の場所を示す一種の目印のようなものです。

caseラベルには、定数整数を書き、これが条件の整数値と一致すれば、その部分の処理が実行されます。 「整数」と書いているように、switch文は整数しか扱えません

整数しか使えないと言ったのに、先ほどのサンプルプログラムでは文字を使っていました。 実は、文字の正体は整数なのです。この辺りは、第20章で取り上げます。

もし、どの caseラベルにも一致しなければ、 defaultラベルが実行されます。 ただし、defaultラベルについては省略が可能ですdefaultラベルが省略された場合は、どの caseラベルにも一致しなければ、何もせずに switch文全体から抜け出します。 しかし、後で書きますが、個人的には defaultラベルは省略しないことを勧めます。

defaultラベルを最後に書かないといけないというルールはありません。 caseラベルと混ぜて書いても構わないし、先頭にもってきても構いません。 一般的には、defaultラベルは「デフォルト」という名前とは裏腹に、「その他」という意味合いであることが多いので、最後に書く場合が多いでしょう。

なお、caseラベルに関しては、最大で 257個まで書けることになっています。 それ以上の個数が使えるかどうかは、コンパイラ次第です。

switch文に関係するラベルは、case と default だけですが、他の単語を使ってラベルを記述できてしまいます。 このようなことは本来不要なはずです。 この仕様は、「default」をうっかり「defualt」のように書き間違えるようなミスを許してしまいます。


各ラベルには、 break文があります。 break文が実行されると、switch文全体から抜け出します。 break文を caseラベルや defaultラベルの最後の部分に書いておくことによって、正しく switch文が抜け出せます。 ただし、一番最後の break文だけは無くても構いません(あってもなくても、switch文の末尾に来ているので、自然に抜け出せます)。 しかし、これについても、個人的には省略しないことを勧めます。

実は、break文は省略できます。 しかしこれは、省略しても同じ意味になるということではなく、意味ごと変わってしまいます。 break文がないと、switch文から抜け出さず、次のラベル(他の caseラベルや defaultラベルのこと)にまで進んでいきます。 これについては後で触れます(⇒ジャンプ)。

フォールスルー

switch文のところで、break文を省略できると書きました。 break文が無いと、次のラベルへ処理が進みます。言い換えると、別の条件の処理へ侵入していることになります。 この様子が、ソースコード上では、上から下へ落ちていくように見えることから、フォールスルーと呼びます。

一例として、先ほどから使っているサンプルプログラムを先に書き変えてみます。

#include <stdio.h>

int main(void)
{
	char in;
	char str[10];

	puts( "質問の答えを入力して下さい。(y/n)" );
	fgets( str, sizeof(str), stdin );
	sscanf( str, "%c", &in );

	switch( in ){
		case 'y':	/* fall through */
		case 'Y':
			puts( "Yes" );
			break;

		case 'n':	/* fall through */
		case 'N':
			puts( "No" );
			break;

		default:
			puts( "Others" );
			break;
	}

	return 0;
}

実行結果:

質問の答えを入力して下さい。(y/n)
Y
Yes

caseラベルに、大文字の 'Y' と 'N' を追加しました。 小文字を入力しても、大文字を入力しても、同じ処理を実行したいので、これをフォールスルーを使って実現しています。

フォールスルーは1つのテクニックとみなせないこともありませんが、まず必要になる機会はありません。 悪いことに、break文をうっかり書き忘れた場合でも、フォールスルーとして有効な構文であるため、コンパイルエラーになってくれません。 これはむしろ、バグを誘発する可能性を高くしているとも考えられます。 今回のサンプルにしても、次章で説明する方法を使って if文で書き変えることができます。

switch文に関しては、個人的には次の基本方針を守ることを勧めます。

ここまで気を使わないといけないのは、それだけ危険性があるということです。 switch文絡みのバグは、各所でよく取り上げられています。 原則、switch文絡みで「省略可能」なものは、「省略しない」方が安全です


練習問題

問題@ 標準入力から受け取った整数が、「0」「0より大きい」「0より小さい」のいずれに該当するかを判定し、 その結果を出力するプログラムを作成して下さい。

問題A 標準入力から、0〜6 の整数を受け取り、それぞれに該当する曜日を出力するプログラムを switch文を使って、作成して下さい。 「0」を日曜日、「1」を月曜日…といった具合に考えるものとします。

問題B 問題Aを、switch文ではなく、if-else の連続で書き換えて下さい。

問題C 次のプログラムはどんな処理をしているのか答えてください。

#include <stdio.h>

int main(void)
{
	int num;
	char str[10];
	
	puts( "1〜4の数字を入力して下さい" );
	fgets( str, sizeof(str), stdin );
	sscanf( str, "%d", &num );
	
	switch( num ){
		case 4:
			printf( "*" );
		case 3:
			printf( "*" );
		case 2:
			printf( "*" );
		case 1:
			printf( "*\n" );
			break;
		default:
			puts( "入力された数字が不正です" );
			break;
	}

	return 0;
}


解答ページはこちら

参考リンク

更新履歴

'2013/7/13 fall through のスペルミスを修正。

'2009/3/13 新規作成。



前の章へ

次の章へ

C言語編のトップページへ

Programming Place Plus のトップページへ