Menu

C言語の break と continue:仕組みと使いどころ

break はループや switch から抜け、continue は次の周回へ飛ばします。for・while・do-while それぞれでの振る舞い、フラグや goto でネストしたループから脱出する方法、そしてループを追いにくくする誤用。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

ループの流れを変える 2 つの方法

ループの条件はいつ終わるかを決めますが、本当の答えが本体の途中で見つかることもあります。配列を検索していて 3 つ目の要素で目的の値が見つかったなら、残りの 997 個を調べる理由はありません。あるいはリストを処理していて、ある項目が空だったなら、それを飛ばして進めばよいのです。

C言語には、それぞれの場合に対応するキーワードが 1 つずつあります:

  • break - いますぐループから出る。実行はその直後の文から再開されます。
  • continue - この周回だけを打ち切る。次の周回へ進みます。

最初のループは 1 2 3 4 を表示して止まります。2 つ目は 1 2 3 4 6 7 8 9 10 を表示します。値を 1 つ飛ばして、そのまま続いたのです。

ループの中の break

典型的な使い方は、成功した時点で止まる検索です:

foundbreak の前に設定されるので、ループの後のコードは「見つかった」と「要素を使い切った」を区別できます。結果を入れる変数と break の組み合わせ、これが C言語の標準的な検索イディオムです。ループカウンタ自体はヘッダーで宣言されている場合、閉じ波かっこでスコープを抜けるため、あとから使うことはできません。

break が抜けるのは常に1 つのループ、つまり最も近い forwhiledo-whileswitch です。

ループの中の continue

continue は、処理を止めるのではなく飛ばしたい項目のためにあります:

continue がどこへ飛ぶかは、ループの種類によって決まります:

for        -> ヘッダーの更新部分(カウンタはきちんと進む)
while      -> 条件判定(本体の残りは実行されない)
do-while   -> 末尾の条件判定

この違いは机上の話ではありません。for ループでは、増加処理がヘッダーにあるので continue がそれを飛ばすことは決してありません。while ループでは飛ばせてしまいます:

/* BUG: continue skips i++, so this hangs at the first even number */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

i++continue より上に移すか、for ループとして書きましょう。for ならヘッダーのおかげで間違えようがありません。

switch の中の break はループの break ではない

break は、最も近くでそれを囲んでいるループまたは switch を終わらせます。ループの中に switch を置くと、その中のすべての break は switch のものになります:

q コマンドはメッセージを表示しますが、ループは cc まで走り続けます。終了がループを終わらせるはずだったのなら、あの break はその役目を果たしていません。直し方はフラグ、goto、あるいはループを関数に移して return を使うことで、いずれも以下に示します。

ネストしたループから脱出する

break 1 つが抜けるのは 1 階層だけです。ネストした 2 つのループの中では内側だけを終わらせ、外側のループは何事もなかったかのように次の周回を始めます。

C言語には break 2 もラベル付き break もありません。正直な選択肢は 3 つです。

フラグ。 移植性があり、分かりやすく、少しうるさい方法です:

フラグは 2 か所に現れます。外側の条件と代入です。それがコストです。ネストが 3 階層になると、これは本当に不快になります。

goto C言語のさんざん叩かれてきたジャンプですが、経験ある C プログラマが言い訳なしに使う唯一の場面がこれです:

ループのすぐ後ろにあるラベルへの前方 goto は、他の言語のラベル付き break とまったく同じことを、1 行で、同期を保つべきフラグなしに実現します。goto の悪評は、後方へのジャンプや他のブロックの途中へ飛び込むジャンプが稼いだものです。それらは確かに読めない制御フローを生みます。ネストしたループから外へ出る 1 回の前方ジャンプは、それとは違います。Linux カーネルも後始末の経路で同じパターンを使っています。意図的に、控えめに使いましょう。タブー意識に押されて、より読みにくいフラグ 3 つの代替案へ逃げ込まないでください。

関数と return たいていはこれが最良の答えです。検索には名前がふさわしいからです:

return は関数内のすべてのループを巻き戻し、フラグもラベルも要りません。しかも検索するコードに名前が付き、単体でテストできるようになります。ネストが深くて脱出が問題になるほどなら、それはたいてい、そのブロックがもともと関数になりたがっていたという合図です。

役に立つとき、害になるとき

breakcontinue が価値を発揮するのは、本体全体を if で包む代わりに、ループが本体の上のほうで一度「この場合はもう終わり」と言えるようにするときです。比べてみましょう:

/* with continue: the real work is not indented */
for (int i = 0; i < n; i++) {
    if (!isValid(a[i])) continue;
    if (isDuplicate(a[i])) continue;
    process(a[i]);
}

/* without: every guard adds a level */
for (int i = 0; i < n; i++) {
    if (isValid(a[i])) {
        if (!isDuplicate(a[i])) {
            process(a[i]);
        }
    }
}

害になるのは、数が多いときです。break が 5 つ散らばり continue が 3 つあるループは、終了条件がどこにも書かれていません。読む人は、いつ止まるのかを知るために本体全体を頭の中で実行しなければなりません。よく機能する指針が 2 つあります。continue によるガードは本体の先頭にまとめること。そして break が 1 つか 2 つを超えるなら、その仕事はヘッダーの条件がやるべきだったのではないかを考えることです。

小さめの罠が 2 つあります。do-while の中の continue は末尾の判定へ飛びますが、そこはどのみち次に来る場所です。害はないものの、人を驚かせます。そして、ループや switch の外にある break はコンパイルエラーになります。ここで扱った間違いのうち、コンパイラが捕まえてくれる唯一のものです。

よくある質問

C言語の break と continue の違いは何ですか?

break はループを完全に終わらせ、そのすぐ後ろから実行が再開されます。continue は現在の周回だけを打ち切り、次へ進みます。for ループなら更新の部分へ、whiledo-while なら条件判定へ直接飛びます。

C言語でネストしたループから抜けるには?

break 1 つでは最も内側のループから出るだけです。両方から抜けるには、フラグを立てて外側のループの条件で判定するか、goto でループの後ろのラベルへ飛ぶか、あるいは - たいていこれが最もきれいですが - ループを関数に移して return します。

break はループから抜けますか、それとも switch だけですか?

最も内側でそれを囲んでいるほうです。ループの中の switch の中にある breakswitch だけを終わらせ、ループは続きます。switch の中からループを抜けるには、フラグか gotoreturn が必要です。

なぜ while ループの中の continue で無限ループになるのですか?

continue は条件判定へ飛び、本体の残りをスキップするからです。カウンタの増加が本体の最後にあるなら、それも飛ばされます。for ループでは更新がヘッダーにあるので必ず実行されますが、while ループでは違います。更新を continue より前に移すか、for を使いましょう。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める