switch文による多分岐選択

switch は Ruby の case と同様の多分岐選択だ。一般的な形式は次の通り。

switch (値) {
    case 定数1:
        文;
        ....
        break;
    case 定数2:
        文;
        ....
        break;
    default:
        文;
        ....
        break;
}

注意点といえば、

  • 値の比較は等価かどうかしか判定できないこと
  • 値には int か char しか使えないこと
  • case のあとの一連の文には { } が不要なこと
  • 一連の文の最後には break; が必要なこと

break; がないと break; に(あるいは switch の最後に)行き当たるまで文が実行されてしまう。これを利用する書き方もあるようだけど、あまりいいとは思えない。

次のプログラムは、入力された整数に対応する単語を出力する。

#include

int main(void)
{
    int i;

    printf("Input integer: ");
    scanf("%d", &i);

    switch (i) {
        case 1:
            printf("ONE\n");
            break;
        case 2:
            printf("TWO\n");
            break;
        case 3:
            printf("THREE\n");
            break;
        default:
            printf("MANY\n");
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_9
Input integer: 2
TWO
takatoh@nightschool $ ./sample_3_9
Input integer: 4
MANY

continue文

ループの中で continue 文に出会うと、それ以降の処理を飛ばして次の回のループに移る。この時、for ループではインクリメント部が実行されてループ変数は変更される。

次のプログラムは、ユーザーに整数を複数入力させてその合計を出力する(0 で入力終了)。その際、整数を2回入力させて合致するかを確かめている。もし合致しない場合は入力された数を合計に加算せずに continue で次のループに処理が移る。

#include

int main(void)
{
    int total, i, j;

    total = 0;
    do {
        printf("Input number (0 to exit): ");
        scanf("%d", &i);
        printf("Once more: ");
        scanf("%d", &j);
        if (i != j) {
            printf("Error: Try again.\n");
            continue;
        }
        total = total + i;
    } while (i);

    printf("TOTAL: %d\n", total);

    return 0;
}

実行例:

Input number (0 to exit): 1
Once more: 1
Input number (0 to exit): 5
Once more: 5
Input number (0 to exit): 9
Once more: 9
Input number (0 to exit): 0
Once more: 0
TOTAL: 15

実行例2(わざと間違った例):

takatoh@nightschool $ ./sample_3_8
Input number (0 to exit): 1
Once more: 2
Error: Try again.
Input number (0 to exit): 1
Once more: 1
Input number (0 to exit): 5
Once more: 5
Input number (0 to exit): 0
Once more: 0
TOTAL: 6

最初の入力(1, 2)は合致しないので、合計に加算されていない。

break文

break 文はループを脱出する。ただし、break 文の書いてあるループだけだ。

次のプログラムは、内側のループで 1 からインクリメントしながら while ループを回しているけど、5 を超えた時点で break している。外側のループは脱出しないので、1 から 10 までループが回る。

#include

int main(void)
{
    int i, j;

    for (i = 1; i <= 10; i++) {
        j = 1;
        while (j <= 10) {
            if (j > 5) {
                break;
            }
            printf("%d", j);
            j++;
        }
        printf("\n");
    }

    return 0;
}

実行結果:

takatoh@nightschool $ ./sample_3_7
12345
12345
12345
12345
12345
12345
12345
12345
12345
12345

ループのネスト

for、while、do ループはネストすることができる。ネストするループは外側とは別のループでもいい。

次のプログラムは三重の for ループを使っている。アルファベットの A から Z までを2回ずつ、それを3回出力する。

#include

int main(void)
{
    int i, j, k;

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 26; j++) {
            for (k = 0; k < 2; k++) {
                printf("%c", 'A' + j);
            }
        }
    }
    printf("\n");

    return 0;
}

実行:

takatoh@nightschool $ ./sample_3_6
AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZAABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZAABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ

doループ

do ループの一般的な形式。

do {
    文;
    ....
} while (条件式);

条件式が真のあいだループするのは while ループと似ているけど、あと判定なので条件式が偽でも1回は必ずループする。最後にセミコロンが必要なので注意。

前のエントリのプログラムを do ループで書くとこうなる。

#include

int main(void)
{
    char ch;

    do {
        ch = getchar();
    } while (ch != 'q');

    printf("q was given.\n");

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_5
a
b
x
q
q was given.

whileループ

while ループの一般的な形式。

while (条件式) {
    文;
    ....
}

次のプログラムは q が入力されるまで、ループする。

#include

int main(void)
{
    char ch;

    ch = getchar();

    while (ch != 'q') {
        ch = getchar();
    }

    printf("q was given.\n");

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_4
a
b
x
q
q was given.

練習問題3.3

「独習 C」の練習問題 3.3 から。
ループ変数を 1 から 1000 まで、2倍ずつ変更しながら出力する。結果として、等比数列が得られるプログラム。

#include

int main(void)
{
    int i;

    for (i = 1; i <= 1000; i = i * 2) {
        printf("%d ", i);
    }
    printf("\n");

    return 0;
}
takatoh@nightschool $ ./practice_3_3_3
1 2 4 8 16 32 64 128 256 512

forループふたたび

C の for ループは柔軟にできている。初期設定部でループ変数を初期化しなくてもいいし、インクリメント部でインクリメントしなくてもいい。他にもいろいろな書き方ができる。例を見ながら試してみよう。

条件判定部にループ変数以外を使う例

#include

int main(void)
{
    int i;
    char ch;

    ch = 'a';

    for (i = 0; ch != 'q'; i++) {
        printf("Times: %d\n", i);
        ch = getchar();
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_3a
Times: 0
a
Times: 1
Times: 2
b
Times: 3
Times: 4
q

なんか思ったのと違うな……ああ、そうか。文字を取得するのに getchar() を使ってるから入力した a とか b のほかに改行文字を取得してループが回ってるのか。たぶん getche() を使えればこうはならないんだろう。

初期設定部が空の例

for 文の中の式は空でもいい。次の例では、初期値を入力から受け取っているので、for の初期設定部が空になっている。

#include

int main(void)
{
    int i;

    printf("Input positive integer: ");
    scanf("%d", &i);

    for (; i; i--) {
        printf("%d\n", i);
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_3b
Input positive integer: 5
5
4
3
2
1

ターゲットが空の例

ターゲット(for ループで実行する文)が空でもいい。次の例では q が入力されるまでループが回り続ける。初期設定部とインクリメント部で入力を受け付けているのにも注目。

#include

int main(void)
{
    int ch;

    for (ch = getchar(); ch != 'q'; ch = getchar()) {
        ;
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_3c
a
b
x
q

無限ループ

初期設定部、条件判定部、インクリメント部をすべて空にすると無限ループになる。条件判定部に式がないと、コンパイラはそれを真と仮定するようだ。

for ( ; ; ) {
    ....
}

インクリメントをターゲット内で行う例

ループ変数をインクリメント部以外で変更しても構わない。次の例ではターゲット内で変更しているため、インクリメント部が空になっている。

#include

int main(void)
{
    int i;

    for (i = 0; i < 10; ) {
        printf("%d\n", i);
        i++;
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_3d
0
1
2
3
4
5
6
7
8
9

if文のネスト

if 文はネストすることができる。

if (count > max)
    if (error)
        printf("Error: Try again.\n");

else に続いてさらに if がネストするのを if-else-if の梯子(if-else-if ladder)とか if-lese-if の階段(if-else-if staircase)などと呼ぶらしい。
一般的には次の通り。

if (式)
    文;
else
    if (式)
        文;
    else
        if (式)
            文;
        else
            文;

ただし、これだとインデントがむやみに深くなるので、普通は次のように書く。

if (式)
    文;
else if (式)
    文;
else if (式)
    文;
else
    文;

もっと言えば、たとえ文が1つしかなくてもコードブロックを作ったほうが見やすいと思うな。

次のプログラムは、円、長方形、三角形のいずれかを選択して、その面積を計算する。

#include

int main(void)
{
    char ch;
    float x, y;

    printf("Choice: Circle(C), Rectangle(R), Triangle(T): ");
    ch = getchar();

    if (ch == 'C') {
        printf("Radius? ");
        scanf("%f", &x);
        printf("%f\n", x * x * 3.14);
    } else if (ch == 'R') {
        printf("X? ");
        scanf("%f", &x);
        printf("Y? ");
        scanf("%f", &y);
        printf("%f\n", x * y);
    } else if (ch == 'T') {
        printf("Width? ");
        scanf("%f", &x);
        printf("Height? ");
        scanf("%f", &y);
        printf("%f\n", 0.5 * x * y);
    } else {
        printf("Error.\n");
    }

    return 0;
}

実行例:

takatoh@nightschool $ ./sample_3_2
Choice: Circle(C), Rectangle(R), Triangle(T): C
Radius? 2.5
19.625000
takatoh@nightschool $ ./sample_3_2
Choice: Circle(C), Rectangle(R), Triangle(T): R  
X? 2.0
Y? 3.5
7.000000
takatoh@nightschool $ ./sample_3_2
Choice: Circle(C), Rectangle(R), Triangle(T): T
Width? 4.0
Height? 3.0
6.000000
takatoh@nightschool $ ./sample_3_2
Choice: Circle(C), Rectangle(R), Triangle(T): A
Error.