Menu

C言語のwhileループ:構文、例、よくある間違い

C言語のwhileループが条件の変化まで繰り返す仕組み - 条件が先という規則、番兵ループ、EOFまでの入力読み取り、while(1)、そして for と while の相互変換。

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

回数を決めずに繰り返す

for ループは、何周必要かが分かっているか計算できるときの正しい道具です。しかし多くの仕事には回数が付いてきません。ファイルの全行を終わりまで読む。正しくなるまでパスワードを尋ね続ける。1に達するまで数を半分にし続ける。これらでの問いは「何回」ではなく「いつまで」であり、それこそ while が表現するものです。

誰も前もって「7」と計算していません。ループは条件が成り立つ間、ただ進み続けるだけです。

構文と、条件が先という規則

while (condition) {
    // 本体 - 条件が非ゼロの間は実行される
}

条件は最初の周回を含め、毎回の周回の前に判定されます。帰結が3つあります。

  • 条件が最初から偽のループは0回実行されます。while (0) { ... } は本体を一度も実行しません。
  • 条件が判定するものは何であれループの前に初期化されていなければ、ゴミの値を判定することになります。
  • 本体の中の何かがいずれ条件を偽にしなければ、ループは終わりません。

if と同じくかっこは必須で、そのあとにセミコロンは付きません。その迷子のセミコロンは同じバグのループ版です。

/* BUG: 空文が本体になる - これは永久に回り続ける */
while (i < 10);
{
    printf("%d\n", i);
    i++;
}

i は決して増えないので、条件も決して変わりません。gcc -Wall は空の本体を指摘します。

Cの真偽の規則は ifelse とまったく同じくここでも当てはまります。式が非ゼロの間ループは続き、ゼロになると止まります。

標準の形:初期化、判定、更新

正しい while ループのほとんどは、3か所に散った3つの部品を持っています。

これは for のヘッダーが抱える3つの部品と同じもので、ただ1行にまとめられていないだけです。だからこそ「for か while か?」への正直な答えはこうなります。3つの部品がすべて1つのカウンタについてのものなら、for はそれらを読み手が一緒に確認できる場所に置いてくれます。更新が長い本体の中に散らばっていたり、条件が数え上げ以外のものについてなら、while のほうが明快です。

部品3を忘れることがまさにwhileループのバグです。ループが止まらなくなったら、最初に確認すべきは、本体が本当に条件の読むものを変えているかどうかです。

番兵ループ

番兵(sentinel)とは「止まれ」を意味する特別な値です。ユーザーが 0 を入れるまで数を足していきましょう。

0 のあとの 99 には決して到達しません - 番兵がループを終わらせました。数の個数が前もって分からないことに注目してください。まさにそれがこれを while にする理由です。

C言語の文字列も同じように働きます。'\0' という番兵で終わるので、それを歩くのは教科書どおりの while です。

EOF まで入力を読む

実務で最もよくある while は、入力がなくなるまで読むものです。規則は、格納された値ではなく入力関数の戻り値を判定することです。

getchar は次の文字を返し、入力が尽きると定数 EOF を返します。

重要な細部が2つ。cint なのは、EOF が多くのシステムで char に収まらない負の値だからです - char と宣言すると比較が決して成立しなくなりえます。そして代入が条件の中に入っています。(c = getchar()) != EOF は1文字を読み、格納し、比較するのを一歩で行います。内側のかっこは必須です。!== より強く結びつくので、なければ比較の結果が c に格納されてしまいます。ここは、条件の中の代入がバグではなく慣用的である数少ない場所の1つです。

同じ形は scanf でも働きます。その戻り値は変換に成功した項目の個数です。

int n;
while (scanf("%d", &n) == 1) {
    /* n は妥当な数を保持している */
}

このループはファイル終端で終わり、かつ数でない最初の入力でも終わります。たいていそれが望ましい挙動です。

書いてはいけないのは while (!feof(f)) です。feof は読み取りがすでに失敗したかどうかを報告するので、最後の項目の上にいる時点ではまだ偽であり、ループの本体がもう一度走り、その読み取りが失敗し - 古いデータを処理してしまいます。読み取り自体を判定しましょう。

while (1) と break

ときには、抜ける条件が本体の途中でしか分からないことがあります - 読んだあと、処理する前とか。慣用的な答えは、明示的な脱出を伴う意図的な無限ループです。

while (1) は常に真なので、抜ける唯一の道は break(あるいは returnexit)です。同じ目的に for (;;) が使われるのも見かけます。どちらも同じものにコンパイルされ、どちらをコードベースが好むかは流儀の問題です。

抜けるための判定が本当に本体の途中に属するときは、この形を使いましょう。判定が自然に先頭に収まるときは、読み手に見えるよう条件に置きましょう - 唯一の break が最初の文である while (1) は、姿を変えただけの条件です。

for と while の相互変換

どの for ループも while ループとして書けますし、その逆もできます。対応は機械的です。

/* for */                          /* 等価な while */
for (init; cond; update) {         init;
    body;                          while (cond) {
}                                      body;
                                       update;
                                   }

だからこの2つは同じものを表示します。

この等価性が漏れる唯一の場所が continue です。for ループの中の continue はヘッダーの更新へ飛ぶので、カウンタは進みます。while 版では continue は条件へ直接戻り、本体の末尾の i++ を飛ばします - 即座に無限ループです。

/* BUG: continue が i++ を飛ばすので、最初の偶数で永久に回り続ける */
int i = 0;
while (i < 10) {
    if (i % 2 == 0) {
        continue;
    }
    printf("%d ", i);
    i++;
}

while ループで continue を使うなら、更新がその前に行われるようにするか、ヘッダーがそれを保証してくれる for を使いましょう。

よくある落とし穴

  • 条件を更新するものがない。 本体は条件が読むものを変えなければなりません。プログラムが止まらなくなったら、まずここを見ましょう。
  • ヘッダーのあとのセミコロン。 while (cond); は空文を本体にします。
  • 一度も初期化されていない変数を判定する。 条件は本体より前に走るので、それが読む値はすでに存在していなければなりません。
  • getcharchar を使う。 EOFchar に収まりません。比較が決して真にならないか、正当なバイトが EOF と誤解されます。
  • while (!feof(f)) 1回多く回ります。読み取りを判定しましょう。
  • 本体が少なくとも1回は走ると思い込む。 走りません。保証された1周が必要なとき - 選択を確認する前にメニューを表示するなど - それは do-while ループです。

よくある質問

C言語でwhileループはどう書きますか?

while (condition) { body } です。条件は毎回の周回の前に判定されるので、本体は条件が非ゼロの間だけ実行され、最初から偽ならまったく実行されません。本体の中の何かがいずれ条件を変えなければ、ループは終わりません。

C言語の for と while の違いは何ですか?

能力は同等で、どの forwhile に書き換えられます。カウンタとその上限と刻みが1つのヘッダーにまとまるときは for を、停止条件が数え上げでないとき - ファイル終端まで読む、成功するまで再試行する、フラグが変わるまで回る - は while を使いましょう。

C言語でEOFまで入力を読むには?

読み取った値ではなく、入力関数の戻り値を判定します。while (scanf("%d", &n) == 1) { ... } はファイル終端か最初の数字でない入力で止まり、while ((c = getchar()) != EOF) { ... } は1文字ずつ読みます。while (!feof(f)) でループしてはいけません - 末尾を越えて1回余分に回ります。

なぜ私のC言語のwhileループは無限になるのですか?

たいていは、本体の中に条件が判定している値を変えるものがないか、ヘッダーのあとの迷子のセミコロン(while (i < 10);)が空文を本体にしてしまったからです。どちらも問題なくコンパイルされるので、本体が本当に条件の変数を更新しているか確認しましょう。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める