[C言語入門]複数の分岐をswitchでシンプルに記述する

前回、if文による処理の分岐を説明しました。
C言語によるプログラムではifさえあればあらゆる分岐は記述できるのですが、場合によってはifの羅列では分かり辛くなってしまう場合も有ります。

例えば、以下のようにメニューを用意し入力値を判定させる場合、比較対象は常にdataであるにも関わらず、毎回”data ==”を書かなければなりません。無駄な表記があると書き間違いが発生しやすくなりますし、ソースコードも見づらくなります。

#include <stdio.h>
 
int main()
{
    int data;
 
    printf( "メニュー\n" );
    printf( "--------------------\n" );
    printf( "1:足し算\n" );
    printf( "2:引き算\n" );
    printf( "3:掛け算\n" );
    printf( "4:割り算\n" );
    printf( "\n" );
 
    printf( "メニューより番号を入力してください >" );
    scanf( "%d", &data );
    if ( data == 1 ) {
        printf( "足し算を行います\n" );
    } else if ( data == 2 ) {
        printf( "引き算を行います\n" );
    } else if ( data == 3 ) {
        printf( "掛け算を行います\n" );
    } else if ( data == 4 ) {
        printf( "割り算を行います\n" );
    } else {
        printf( "入力値が不正です\n" );
    }
}




このような場合は、switch文を使うとすっきりします。
以下のコードは、先ほどのプログラムをswitchで書き直したものです。

#include <stdio.h>
 
int main()
{
    int data;
 
    printf( "メニュー\n" );
    printf( "--------------------\n" );
    printf( "1:足し算\n" );
    printf( "2:引き算\n" );
    printf( "3:掛け算\n" );
    printf( "4:割り算\n" );
    printf( "\n" );
 
    printf( "メニューより番号を入力してください >" );
    scanf( "%d", &data );
 
    switch( data ) {
        case 1:
            printf( "足し算を行います\n" );
            break;
        case 2:
            printf( "引き算を行います\n" );
            break;
        case 3:
            printf( "掛け算を行います\n" );
            break;
        case 4:
            printf( "割り算を行います\n" );
            break;
        default:
            printf( "入力値が不正です\n" );
            break;
    }
}



switch文を使う事で、比較対象の情報が変数”data”である事と、その値が1,2,3,4だった時に処理がある事が分かりやすくなりました。

このプログラムの実行結果は、以下のようになります。

$ gcc -Wall -o test03 test03.c
 
$ ./test03
メニュー
--------------------
1:足し算
2:引き算
3:掛け算
4:割り算
 
メニューより番号を入力してください >1
足し算を行います
 
 
$ ./test03
メニュー
--------------------
1:足し算
2:引き算
3:掛け算
4:割り算
 
メニューより番号を入力してください >2
引き算を行います
 
 
$ ./test03
メニュー
--------------------
1:足し算
2:引き算
3:掛け算
4:割り算
 
メニューより番号を入力してください >4
割り算を行います
 
 
$ ./test03
メニュー
--------------------
1:足し算
2:引き算
3:掛け算
4:割り算
 
メニューより番号を入力してください >5
入力値が不正です
 
 
$ ./test03
メニュー
--------------------
1:足し算
2:引き算
3:掛け算
4:割り算
 
メニューより番号を入力してください >-1
入力値が不正です





今回登場したswitch文ですが、構文は以下の通りです。

switch( 条件 ) {
    case 値1:
        処理内容1
    case 値2:
        処理内容2
    ...
 
    default:
        どのcaseにも一致しない場合の処理
}




switch文の振る舞いについては、以下のルールで処理が実行されます。

Step1
    条件に書かれた式を評価します。
 
Step2
    評価値を元に,各caseの後に書かれた値と一致するcase要素を探します。
 
    等しいものがあった場合、次実行する命令をその場所にジャンプします。
    等しいものがなく,default:があった場合、次実行する命令をdefaultにジャンプします。
    等しいものがなく,default:も無い場合、次実行する命令は,switch後の処理にジャンプします。
 
Step3
    break文が現れるまで、switch文の中の処理を順に処理します。




caseの後にある”値”には、定数のみ記述できます。
以下にcaseとして書ける記述と書けない記述の例を示します。

int x = 2;  // 変数を宣言
 
switch( data ) {
    case 1:                    /* OK: 定数の指定が行える */
        printf( "case1です" );
        break;
 
    case x:                    /* NG: 変数は指定できない !! */
        printf( "case2です" );
        break;
 
    case 1+2:                  /* OK: 式の結果はコンパイル時に確定するので大丈夫 */
        printf( "case3です" );
        break;
 
    case '9':                  /* OK: 文字定数は数値とみなされるのでOK         */
                               /*     '9'はアスキーコードで0x39(10進数だと57)  */
                               /*     なのでcase 57:と書いたのと同じ意味になる */
        printf( "case9です" );
        break;
 
    default:
        //どのcaseにも一致しない場合の処理
        break;
}




まだ説明していない内容ですが、const定数やdefine値をcase値として使用出来るかも説明しておきます。
(ここは、意味が分からない人は読み飛ばしてもらっても結構です)

#define PTN1 1                   /* defineによる定数の定義 */
 
const int PTN2 = 2;              /* constによる変更不可値の定義 */
 
switch( data ) {
    case PTN1:                   /* OK: 定数の指定が行える */
        printf( "case1です" );
        break;
 
    case PTN2:                   /* NG: 変数は指定できない */
        printf( "case2です" );
        break;
 
    default:
        printf( "defaultです\n" );
}





指定された値に応じて処理を分岐させたいので、通常は各case句の最後にbreakが入る事になります。
もし、breakが書かれていないと、次のcase句の処理が実行され行きます。

例えば以下のプログラムを実行すると…

#include <stdio.h>
 
int main()
{
    int data = 2;
 
    switch( data ) {
        case 1:
            printf( "足し算を行います\n" );
            break;
        case 2:
            printf( "引き算を行います\n" );    /* breakを書き忘れた!! */
 
        case 3:
            printf( "掛け算を行います\n" );
            break;
        case 4:
            printf( "割り算を行います\n" );
            break;
        default:
            printf( "入力が不正です\n" );
            break;
    }
 
    return 0;
}




実行結果は以下のようになってしまいます。

$ gcc -Wall -o test03 test03.c
 
$ ./test03
引き算を行います
掛け算を行います


breakの書き忘れによって、プログラムが意図しない動作をするのは初心者によくあるミスなので注意が必要です。


一方、以下のようにあえてbreakを書かずに処理を行う場合もあります。

例1:0、(0以外の)偶数、奇数を判定する

switch (data ) {
    case 0:
        printf( "値が0です\n" );
        break;
 
    case 1:
    case 3:
    case 5:
    case 7:
    case 9:
        printf( "奇数です\n" );
        break;
 
    case 2:
    case 4:
    case 6:
    case 8:
        printf( "偶数です\n" );
        break;
}



例2:プログラムレベルに応じた学習メニューを提示する */

int level;
printf( "C言語の知識レベルを1~3で入力して下さい" );
scanf( "%d," &level );
 
switch ( level ) {
    case 1:
        printf( "入門編のテキストを勉強してください\n" );
        /* FALLTHROUGH */
    case 2:
        printf( "中級編のテキストを勉強してください\n" );
        /* FALLTHROUGH */
    case 3:
        printf( "上級編のテキストを勉強してください\n" );
        break;
    default:
        printf( "入力値が不正です\n" );
        break;
}


後者の例のように、ある処理を行った後、引き続き次の処理を行いたい場合にbreakを省略すると、後から見た人が、このコードはbreakを意図的に省略したのか、それとも書き忘れたのかを、判断できなくなってしまいます。この為、/*FALLTHROUGH*/(下に処理が流れていく)や、/*nobreak*/というコメント等をあえて書いておいてあげたほうが親切です。
企業によっては、この様なコメントを書く事を必須とする社内ルールを設けている場合も多いです。



また、どのcaseにも合致しない場合default句の処理が走ると説明しましたが、defaultのつづりが間違っていてもコンパイルエラーにはなりません。これも気づきにくいミスの1つです。

#include <stdio.h>
 
int main()
{
    int data = 99;  /* defaultに入る事を想定 */
 
    printf( "処理開始\n" );
    switch( data ) {
        case 1:
            printf( "1が指定されました\n" );
            break;
        case 2:
            printf( "2が指定されました\n" );
            break;
        defualt:                            /* defaultのつづりを間違えた!! */
            printf( "入力が不正です\n" );
            break;
    }
    printf( "処理終了\n" );
 
    return 0;
}



実行結果

$ gcc -o test03 test03.c    <= コンパイルは正常に行われる
 
$ ./test03                  <= 実行しても,"入力が不正です"のメッセージが出ない
処理開始
処理終了




また、switch文のdefaultはC言語の文法的には省略可能ですが、習慣として常にdefault処理は書いておいた方が良いです。
仮にプログラムの作り上、defaultに入る可能性がありえない場合でも、default句内でエラーメッセージを表示させておけば、バグがあった場合に気付くのが早くなります。

関連記事

コメントを残す

メールアドレスが公開されることはありません。