3.3 switch 文での制限
C 以外の言語にも switch 文やそれに相当する文がある。しかし、普通、条件となる変数は整数以外でも大丈夫でよく文字列なども使われる。 一方、C では条件となる変数は整数型でなければならない。(列挙型も実質整数型なのでよく使用される)
あるいは “#define A 0” のようにシンボルを定義しておけば、case A: のように使えるのでプログラムがわかりやすくなる。 従って、次のような switch 文はエラーとなる。
/* エラーとなる switch 文 */
switch (argv[1]) {
case "A":
...
break;
case "B":
...
break;
....
}
この例で文字列でなく文字、例えば “A” でなく ‘A’ にすれば問題ない。つまり文字型は8ビット整数型であるためだ。
3.4 ブール値
古い規格 (C99 より前) の C では bool 型というのはなくて、整数型を bool 型として使っていた。 新しい規格ではヘッダファイル stdbool.h をインクルードすることで、 bool, true, false が使える。ただ、これも実際は整数で true=1, false=0 である。
新しい規格でも整数型をブール値の代わりに使うことができる。その際、0 が false, 非 0 が true とみなされる。
次に stdbool.h の使用例を示す。このソースをビルドするには、-std=c11 オプションを付ける。
さらに新しい規格 C23 では bool, true, false がキーワードとなったため stdbool.h をインクルードする必要がなくなった。(その代わり gcc では -std=c2x オプションが必要)。
#include <stdbool.h>
#include <stdio.h>
void main() {
bool b = false;
printf("%d, %d\n", b, !b);
}
実行例
$ bin/stdbool
0, 1
3.5 ループ
C ではループを作るのに、for, while, do .. while がある。残念なことに foreach はない。
3.5.1 無限ループ
for 文のカッコ内は3つの部分に分かれているが、真ん中の部分がループを続けるかどうかを判断する式である。 この式の値が偽 (= 0) ならループを抜けるが、この部分を省略するとループを抜ける条件がないことになり、 無限ループを実現できる。その場合、他の部分も空欄で構わないので、次の例のようにすると無限ループを実現できる。
無限ループからの脱出には、ある条件が成り立ったとき、break 文を実行する。
#include <stdio.h>
void main() {
int i = 10;
for (;;) {
if (--i) {
printf("%d\n", i);
}
else {
break;
}
}
}
while 文で無限ループを実現した方が直感的である。while 文のカッコ内の式が偽になるとループを脱出するので、この部分を true (あるいは非ゼロ) にすると、偽になることがないので無限ループになる。
#include <stdio.h>
#include <stdbool.h>
void main() {
int i = 10;
while (true) {
if (--i) {
printf("%d\n", i);
}
else {
break;
}
}
}
3.5.2 変則型 for 文
for 文のカッコ内は3つの部分からなるが、真ん中の比較式を除いて複数の文を書くことができる。また、各部の内容は省略できるし、for 文の外に書くこともできる。
次の例は、初期化部と更新部に複数の文を置いた例である。
#include <stdio.h>
void main() {
int i, j;
for (i = 0, j = -5; i < 6; i++, j++) {
printf("i=%d, j=%d\n", i, j);
}
}
次の例は初期化部を省略し、その代わりに for 文の外で変数を初期化している例である。
#include <stdio.h>
void main() {
int i = 0;
int j = -5;
for (; j < 3; i++) {
printf("i=%d, j=%d\n", i, j);
j++;
}
}
下の例は、for 文としての意味はないが、繰り返しを行わない for 文である。
#include <stdio.h>
void main() {
int i = 0;
int j = -5;
for (i = 1, j = 2; 0;);
printf("i=%d, j=%d\n", i, j);
}
3.5.3 深いループからの脱出
ループからの脱出には普通 break 文を使うが、深いループ内でエラーが発生したときのようにループの一番外へ脱出したい場合には goto 文を使用する。
#include <stdio.h>
void main() {
int i, j;
for (i = 0; i < 5; i++) {
for (j = 0; j < 10; j++) {
if (i > 2) {
goto LABEL;
}
}
}
LABEL: printf("i=%d, j=%d\n", i, j);
}
下はこのプログラムの実行例である。
$ ./bin/goto
i=3, j=0
longjmp() 関数をコールして setjmp() 関数で設定した位置へロングジャンプする方法もある。この方法だと、ループ外だけでなくメイン関数などへも戻れる。
次のコードは関数 test() 内の二重ループから直接、メイン関数へ戻る例である。
/* setjmp / longjmp */
#include <stdio.h>
#include <setjmp.h>
#include <stdbool.h>
void test();
jmp_buf jumpenv;
volatile int flag;
/* main */
int main() {
if (setjmp(jumpenv) == 0) {
// 普通に setjmp 関数が実行された後
flag = false;
printf("setjmp: flag=%d\n", flag);
test();
}
else {
// longjmp 関数が実行された後
printf("longjmp: flag=%d\n", flag);
}
}
/* 二重ループから脱出し、main へもどる。 */
void test() {
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
if (i == 50 && j == 50) {
// setjmp() がコールされた状態を復元し、setjmp() の戻り値として 1 を設定する。
flag = true;
longjmp(jumpenv, 1);
}
}
}
}
実行例
$ ./bin/longjmp
setjmp: flag=0
longjmp: flag=1
$