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

先頭へ戻る

この章の概要

この章の概要です。

else-if

前章で見たように、if文は処理の流れを2方向に分岐させることができます。 else というキーワードを使おうと使うまいと、分岐する方向は2方向にしかなりません。 しかし、回答の仕方が「はい」「いいえ」「どちらともいえない」になる質問のように、 3方向以上に分岐させたい場面もあります。

処理の流れを3方向以上に分岐させる方法は2つあります。 まずは、これまで通りに 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 の条件式を満たさなかったら else の方へ進み、そこには新たな if が登場する、という形です。 そのため、2つ目の if文が、普通の if文の規則どおりに実行されます。

2つ目の if文では、'n' との一致を調べており、一致すれば { } の中へ、一致しなければ最後の else へ進みます。

4つ以上の分岐が必要であれば、同じ要領で else if を繰り返して記述します。 なお、最後が単独の else で終わらなければならない訳ではありません。 「その他の場合」の処理が必要なければ、単独の 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ラベルに関しては、最大で 257個まで書けることになっています。それ以上の個数が使えるかどうかは、コンパイラ次第です。

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

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

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

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

ラベルという仕組み自体は、switch文にだけ存在するものではなく、単独で使える機能です。そのため、「case」や「default」以外のラベルを、うっかり switch文で使っても許されてしまいます。このことは例えば、「default」を書き間違えて「defualt」などと書いてしまっても、コンパイラがミスを発見しないということですから、注意が必要です。


各ラベルで行う処理の終わりには、 break文があります。break文が実行されると、switch文全体から抜け出します。break文を caseラベルや defaultラベルのところで行う処理の終わりに書いておくことによって、処理の終わりをきちんと検出して、switch文が抜け出せます。

ただし、switch文の } の直前、つまり一番最後の break文だけは無くても構いません。あってもなくても、switch文の末尾に来ているので、自然に抜け出します。しかし、これについても、個人的には省略しないことを勧めます。

break文は省略できます。省略しても同じ意味になるということではなく、意味ごと変わってしまいます。この話題は、次の項で取り上げます

フォールスルー

break文がない場合、あるラベルでの処理を終えても、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':	/* 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' を追加しました。 小文字を入力しても、大文字を入力しても、同じ処理を実行したいので、これをフォールスルーを使って実現しています。

例えば、'y' が入力された場合、「case 'y':」のところが実行されますが、そこには break文がなく、 そのまま直後に書かれている「case 'Y':」のところへ処理が侵入します。 結局、'y' を入力しても、'Y' を入力しても、同じ処理が実行されます。

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

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

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


練習問題

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

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

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

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

#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;
}


解答ページはこちら

参考リンク

更新履歴

'2018/2/6 全面的に文章を見直し、修正を行った。

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

'2009/3/13 新規作成。





前の章へ(第11章 処理の流れを分岐させる)

次の章へ(第13章 複数の条件で分岐させる)

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

Programming Place Plus のトップページへ


このエントリーをはてなブックマークに追加
rss1.0 取得ボタン RSS