🔀 制御構造
🎯 無限ループの原因を3つの型で診断でき、意図的な無限ループを出口つきで安全に書ける
実行したら、画面が固まって返ってこない。カーソルだけが点滅している。初めてこれに出くわすと「壊した」と思うものですが、たいていは無限ループです。壊れてはいません。怖がるより、原因の型を覚えたほうが早く片付きます。
まず止め方から。実行中のコンソールで Ctrl + C を押せば中断できます。これを知っているだけで恐怖は半分になります。
無限ループの原因は、ほぼ3つの型に収まります。
型1: 更新を忘れた。 whileの中で、条件に使っている変数を動かしていない。
#include <stdio.h>
int main(void) {
int i = 0;
while (i < 5) {
printf("%d ", i);
// i++; ← これが無いと 0 が出続ける
}
return 0;
}型2: 更新はしているが、条件に近づかない。 i-- と書くつもりが i++ になっている、i = i + 1 のつもりが i + 1 だけ(計算しても代入していない)、あるいは条件がそもそも絶対に偽にならない(while (x != 3) に対して x が 2 ずつ増えていて 3 を飛び越す、など)。
型3: continueが更新を飛ばした。 前のトピックで見た while + continue の地雷です。
診断のコツは、疑わしいループの先頭に printf を1行入れて、条件に使っている変数の値を毎周表示させること。数字が動いていなければ型1か型3、動いているのに条件に近づかないなら型2です。原因が絞れれば、直すのは一瞬です。
ここまで悪者扱いしてきましたが、わざと終わらせないループもあります。ゲームのメインループ、組み込み機器の制御、サーバーの待ち受け——これらは電源が切れるまで回り続けるのが正しい姿です。
#include <stdio.h>
int main(void) {
int count = 0;
while (true) { // C23 なら true をそのまま書ける
count++;
if (count >= 3) {
printf("3回まわったので終わります\n");
break; // 出口は必ず用意する
}
}
return 0;
}while (true) や for (;;) が定番の書き方です。大事なのは、出口を必ず1つ用意すること。「条件で終わらせない」と決めたなら、代わりに break で終わらせる責任が生じます。出口の無い while (true) は、ただのバグです。
最後に、このトラックの総まとめとして FizzBuzz を書きましょう。1から15まで表示し、3の倍数なら Fizz、5の倍数なら Buzz、両方の倍数なら FizzBuzz にします。
#include <stdio.h>
int main(void) {
for (int i = 1; i <= 15; i++) {
if (i % 15 == 0) {
printf("FizzBuzz\n");
} else if (i % 3 == 0) {
printf("Fizz\n");
} else if (i % 5 == 0) {
printf("Buzz\n");
} else {
printf("%d\n", i);
}
}
return 0;
}forで回数を決めて回し、if / else ifを狭い条件から並べ、剰余で判定する。このトラックで学んだことが全部入っています。もし i % 3 == 0 を先頭に置いたら、15 は Fizz に吸い込まれて FizzBuzz は一度も表示されません。t06-03 で見た「順番の罠」が、ここでも顔を出すわけです。
ループを書く前のチェックを3つ、置いていきます。この条件は、どの行で偽になる? 条件に使う変数は毎周きちんと動いている? 最悪でも何周で終わる? 3つ目に答えられないループには、安全弁のカウンタを入れておくと安心です。
while (true) には break の出口を必ずセットで書く終了条件が満たされることなく、永遠に繰り返され続けてしまうループのこと。意図的に使う場合もあるが、多くはバグとして発生する。for(;;)やwhile(1)のように、あえて意図的に書かれることもある。
条件が真である間、処理を繰り返す構文のこと。何回繰り返すかが実行前には分からない場合に向いた、シンプルな繰り返し構文。条件を書き間違えると、意図せず無限ループになってしまうことがある。
「初期化・条件・更新」の3つを1行にまとめて書ける繰り返し構文のこと。回数があらかじめ決まっている繰り返しにとくに向いている。繰り返す回数があらかじめはっきりしている場面で、とくに書きやすい構文。
if文の条件が偽だったときに実行する処理を書くための構文のこと。「そうでなければ」という日本語の意味にそのまま対応している。elseの後には条件を書かず、if以外のすべての場合を受け止める役割を持つ。
ループやswitch文の実行をその場で強制的に抜け出す命令のこと。「これ以上繰り返す必要がない」と判断したときに使う。多重ループの中で使うと、いちばん内側のループだけを抜ける点に注意が必要。
ループの残りの処理を飛ばして、次の繰り返しへすぐ進む命令のこと。特定の条件のときだけ処理をスキップしたい場面で使われる。スキップされるのはあくまで残りの処理であり、ループ自体は終わらない。
もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。