C言語でくり返し処理を書くとき、最初につまずきやすいのがwhile文です。
条件の書き方を少し間違えるだけで無限ループになり、プログラムが止まらなくなってしまいます。
本記事では、C言語初心者の方でも安心して読めるように、while文の基本構文から、for文との違い、簡単なミニゲーム作成までを、コメント付きサンプルコードとともに丁寧に解説していきます。
while文とは?条件を満たす間くり返す基本
while文の基本構文と読み方
まずは、while文の基本的な形から確認します。
C言語におけるwhile文は、次のような構文になります。
while (条件式) {
/* 条件式が真(1)の間くり返し実行される処理 */
}
この構文は、文章として読むと「条件式が真であるあいだ、このブロックの中身をくり返す」という意味になります。
C言語では0が偽、0以外が真として扱われるため、条件式の結果が0でない限り、ブロック内の処理が続けて実行されます。
初心者の方は、while文を見るたびに「今、この条件が真なら中身を実行し、終わったらまた条件をチェックする」という流れを頭の中でなぞるように読むと理解しやすくなります。
条件式が真の間くり返すしくみ
while文のくり返しは、次のような順序で動作します。
1つ目に、最初に条件式が評価される点が重要です。
条件式が真(0以外)であればブロック内の処理が1回実行されます。
その後、ブロックの末尾まで実行し終えると、もう一度条件式が評価され、再度真ならまた処理が実行されます。
つまり、while文の動作は「条件のチェック」と「処理の実行」を交互にくり返しているイメージになります。
条件がずっと真であり続ける場合は、永遠に処理がくり返されるため、これが無限ループの原因となります。
条件が偽になるとループが終わる流れ
ループを終わらせるには、いつか条件式が偽(0)になる必要があります。
そのため、while文の内部では、次のようなことが行われるのが一般的です。
まず、ループの回数を数えるカウンタ変数を用意し、その値を少しずつ変化させます。
そして、条件式ではそのカウンタ変数を参照し、ある値に達したところで偽になるようにします。
こうすることで、一定回数だけくり返したあと、自然にループが終了します。
条件が偽になった瞬間、while文からは抜け出し、次の文(whileブロックの直後に書いた文)の実行に移ります。
while文自体は、くり返し処理の範囲だけを担当し、その後の処理は通常の文として続けられるという流れになります。
while文が向いている場面の例
while文は、「いつループを終えるかを条件で決めたい場面」に向いています。
特に、あらかじめ「何回くり返すか」がはっきり決まっていない処理で威力を発揮します。
例えば、ユーザーからの入力を読み取り、0が入力されたら終了する処理を考えます。
この場合、「0が入力されるまで繰り返す」という条件でループを回したいので、回数ベースではなく条件ベースのwhile文が自然に使えます。
また、ファイルの終端(EOF)までデータを読み込む処理や、ネットワークからのデータ受信が成功している間くり返す処理のように、外部環境によって終了タイミングが変わる処理もwhile文の典型的な用途です。
C言語初心者がつまずきやすいポイント
C言語初心者の方は、while文で次のような点につまずきやすいです。
まずよくあるのが、条件式がいつまでも真のままになってしまうケースです。
カウンタ変数を増やし忘れたり、条件式に使っている変数の更新がループ内部に書かれていなかったりすると、条件が永遠に真のままとなり、プログラムが終了しなくなります。
また、条件式に代入演算子=を書いてしまい、比較演算子==と間違えるパターンも多いです。
while (x = 1)と書いてしまうと、変数xに1を代入したあと、その結果(1)が真とみなされるため、常に真になってしまいます。
さらに、ループ内で扱う変数の初期化位置にも注意が必要です。
毎回ループの先頭で初期化してしまい、いつまでたっても条件が変化しないことがあります。
初心者のうちは、どこで変数を初期化し、どこで更新しているかを紙に書いて整理しながらwhile文を読むと理解が進みます。
はじめてのwhile文入門:安全なくり返し処理
最小のwhile文サンプルコード
ここでは、もっとも単純なwhile文の例として、1から5までの数字を表示するプログラムを示します。
#include <stdio.h>
int main(void) {
int i = 1; /* カウンタ変数iを1で初期化 */
/* iが5以下のあいだ、ブロック内をくり返し実行 */
while (i <= 5) {
printf("%d\n", i); /* 現在のiを表示 */
i = i + 1; /* iを1増やす(更新処理) */
}
return 0;
}
1
2
3
4
5
このサンプルは、初期化→条件判定→処理→更新というwhile文の基本パターンがコンパクトに表現されています。
まずiを1に初期化し、i <= 5が真であるあいだprintfを実行し、最後にiを更新することで、5回でループが終了するしくみになっています。
カウンタ変数と条件式の関係
while文の動きを理解するうえで、カウンタ変数と条件式の関係はとても重要です。
上の例では、カウンタ変数iを使って、次のような役割分担がされています。
まず、カウンタ変数iは「今、何回目のループか」を表す目印のようなものです。
次に、条件式i <= 5は、その目印が「どこまで進んだら終わりにするか」を決める役割を持ちます。
そして、ループ内のi = i + 1;は、その目印を1つずつ進める作業です。
この3つの要素、つまり初期値・条件式・更新が互いに矛盾しないように設計されていると、ループは期待どおりの回数だけ実行されます。
たとえば、初期値が1で、条件式がi <= 5、更新がi = i + 1の場合、1,2,3,4,5という5回でループが終了することが容易に説明できます。
逆に言えば、この3つのうちどれか1つでもおかしくなると、回数がずれたり、終了しなかったりという問題が発生します。
while文を書く際には、「初期値→条件式→更新」のつながりをセットで考える癖をつけることが、バグを減らす第一歩です。
無限ループの原因と防ぎ方
while文でもっとも怖いミスは無限ループです。
無限ループとは、ループが永遠に終わらなくなり、プログラムが終了しなくなる状態を指します。
C言語のプログラムは、無限ループに入ると、自分で抜け出すことはできません。
無限ループが起きる典型的な原因は次のようなものです。
まず、カウンタ変数を更新していない場合です。
たとえば、次のコードではiが1のまま変化しないため、条件i <= 5が永遠に真となります。
#include <stdio.h>
int main(void) {
int i = 1;
while (i <= 5) {
printf("%d\n", i);
/* iを増やし忘れているため、無限ループになる */
/* i = i + 1; が必要 */
}
return 0;
}
無限ループを防ぐためには、まず「必ずどこかで条件を変化させる処理が書かれているか」をチェックすることが重要です。
カウンタ変数を使う場合は、ループ内部のどこかに更新処理が入っているかを必ず確認します。
もうひとつの防ぎ方として、デバッグ用の上限回数を設ける方法があります。
たとえば、本来は条件で終了したい処理であっても、一時的に「1000回を超えたら強制終了する」といった安全装置を入れておくと、無限ループが発生したときに早めに気づくことができます。
breakとcontinueの使いどころ
while文の中では、breakとcontinueという2つの制御文を使うことで、ループの動作を柔軟にコントロールできます。
breakは、ループを途中で完全に抜けるための文です。
条件がまだ真であっても、break;に到達した瞬間にwhileループの外へ出ます。
一方、continueは、その回の残りの処理をスキップして次の繰り返しに進むための文です。
次のサンプルでは、1から10までのうち、奇数だけを表示するプログラムを示します。
#include <stdio.h>
int main(void) {
int i = 0;
while (i < 10) {
i = i + 1; /* 先にiを1増やす */
if (i % 2 == 0) {
/* iが偶数なら、この回の残りをスキップして次のループへ */
continue;
}
printf("%d\n", i); /* 奇数だけがここに到達する */
}
return 0;
}
実行結果は次のようになります。
1
3
5
7
9
このように、特定の条件でループを抜けたいならbreak、特定の条件のときだけ処理を飛ばし、ループ自体は続けたいならcontinueという使い分けを意識しておくと、while文の表現力が大きく向上します。
実行順序(初期化→判定→処理→更新)を意識する
while文の誤動作の多くは、処理の実行順序を勘違いしていることに由来します。
while文では、次の順序で処理が進みます。
- ループの外で初期化を行う
- whileの条件式を判定する
- 条件が真なら、ブロック内の処理を上から順に実行する
- ブロック末尾まで到達したら、また2に戻る
- 条件が偽になったら、ループを抜けて次の文へ進む
更新処理(カウンタを増やすなど)は、通常3の「ブロック内の処理」のどこかに書きます。
更新の位置によって、ループ回数や表示される値が変わるため、「いつ変数が変化するか」に敏感になることが大切です。
次の2つのコードを見比べてみると、更新の位置が違うだけで結果が変わることが分かります。
#include <stdio.h>
int main(void) {
int i = 0;
/* パターン1: 先に判定してからiを増やす */
while (i < 3) {
printf("pattern1: i = %d\n", i);
i = i + 1;
}
i = 0; /* iをリセット */
/* パターン2: 最初にiを増やしてから処理する */
while (i < 3) {
i = i + 1;
printf("pattern2: i = %d\n", i);
}
return 0;
}
想定される実行結果は次のようになります。
pattern1: i = 0
pattern1: i = 1
pattern1: i = 2
pattern2: i = 1
pattern2: i = 2
pattern2: i = 3
この例から分かるように、while文を理解するうえでは、「1回のループの中で、変数がどの順番で変化するか」を常に追いかける意識が重要です。
for文とのちがいとwhile文の使い分け
for文とwhile文の基本的な書き方の比較
C言語には、while文のほかにfor文という代表的なくり返し構文があります。
両者は役割が似ていますが、書き方と得意分野が少し異なります。
まず、for文の基本構文は次のようになります。
for (初期化; 条件式; 更新) {
/* 条件式が真(1)の間くり返し実行される処理 */
}
while文とfor文で同じ処理を書くと、次のように対応させることができます。
/* while文を使った書き方 */
int i = 0; /* 初期化 */
while (i < 5) { /* 条件式 */
printf("%d\n", i);
i = i + 1; /* 更新 */
}
/* for文を使った書き方 */
for (int i = 0; i < 5; i = i + 1) {
printf("%d\n", i);
}
このように、while文では初期化と更新をループの外側・内側に分けて書くのに対して、for文ではそれらを1か所にまとめて書くのが大きな違いです。
次の表は、両者の比較を整理したものです。
| 項目 | while文 | for文 |
|---|---|---|
| 初期化の場所 | ループの前で書くことが多い | for文の丸かっこ内の先頭にまとめて書ける |
| 更新の場所 | ループ本体の末尾付近に書くことが多い | for文の丸かっこ内の末尾に書ける |
| 終了条件の表現 | 条件式のみをシンプルに書く | 条件式を初期化・更新とともに一望できる |
| 主な得意分野 | 終了タイミングが条件で変わる処理 | 回数ベース(何回くり返すかが決まっている)処理 |
この表から分かるように、for文とwhile文は基本的な動作は同じですが、「何をどこに書くか」の書式が違うだけだと考えると理解しやすくなります。
「回数が決まっている」ならfor文が有利な理由
ループの回数が最初からはっきり決まっている場合、たとえば10回だけ繰り返すといったケースでは、for文のほうがコードが読みやすくなります。
理由は、for文では初期化・条件式・更新が1行の中にまとめて書かれているため、「どのように回数を制御しているのか」が一目で分かるからです。
読み手はfor文の丸かっこの部分を見るだけで、ループの開始値・終了条件・増減の幅をすぐに把握できます。
一方、while文で同じことを書くと、初期化や更新がループの外側・内側に分かれて書かれるので、コード全体をよく見ないとループ回数の見積もりが難しくなります。
もちろんwhile文でも正しく書けますが、「回数が明確に決まっている処理はfor文で書く」というスタイルを採用すると、プログラム全体がすっきりします。
「いつ終わるかわからない」ならwhile文が便利な理由
逆に、ループを始める時点では「何回くり返すことになるか分からない処理」には、while文が向いています。
このような処理では、終了条件が「特定の値が入力されたとき」や「エラーが発生したとき」のように、データの内容や外部環境によって変化します。
たとえば、「ユーザーが-1を入力するまで数値を読み続ける」といった処理では、何回目で-1が入力されるかはユーザー次第です。
このような場合は、条件式だけを先に書き、内部で状況に応じて変数を更新していくwhile文のほうが自然です。
また、永続的に動き続けるプログラム(サーバーや常駐プログラムなど)でもwhile文がよく使われます。
これらは事実上「ずっと動き続ける」ことが目的であり、終了条件が外部からの命令に依存しているため、while文による条件型のループが適しています。
入力待ちやエラー待ちにwhile文が使われる例
具体的な例として、ユーザーからの入力をチェックしながら処理を続けるプログラムを考えてみます。
次のサンプルでは、ユーザーが0を入力するまで入力を受け付け、その合計を計算します。
#include <stdio.h>
int main(void) {
int value = 0;
int sum = 0;
printf("整数を入力してください(0で終了します): ");
/* ユーザーが0を入力するまでくり返す */
while (1) { /* 条件に1を指定すると「常に真」という意味になる */
if (scanf("%d", &value) != 1) {
/* 入力に失敗したらループを抜ける */
printf("入力エラーのため終了します。\n");
break;
}
if (value == 0) {
/* 0が入力されたらループ終了 */
break;
}
sum = sum + value;
printf("現在の合計: %d\n", sum);
printf("続けて整数を入力してください(0で終了します): ");
}
printf("最終的な合計値: %d\n", sum);
return 0;
}
この例では、while (1)と書くことで、あえて無限ループを作り、その内部でbreakによって抜ける条件を制御しています。
入力エラーや0の入力といった、複数の終了条件がある場合に、このパターンはとても書きやすくなります。
このように、入力待ち・通信待ち・エラー待ちなど、いつ条件が満たされるか事前に分からない処理では、while文による条件型ループが定番となっています。
do while文とのちがいと使い分けの目安
C言語には、while文に似たdo while文という構文も存在します。
do while文の基本構文は次のようになります。
do {
/* 少なくとも1回は実行される処理 */
} while (条件式);
while文とdo while文のもっとも大きな違いは、条件式が評価されるタイミングです。
while文では「最初に条件を判定してから処理を実行」しますが、do while文では「まず処理を1回実行してから条件を判定」します。
そのため、do while文は処理が最低1回は必ず実行されるという特徴を持ちます。
使い分けの目安としては、次のように考えるとよいでしょう。
まず、「条件によっては1回も処理を実行したくない」場合はwhile文を使います。
たとえば「残り回数が0なら何もしない」などの状況では、最初に条件をチェックしてから動くwhile文が適しています。
一方、「必ず1回は処理したうえで、続けるかどうかを決めたい」場合はdo while文が便利です。
代表的な例は、メニュー表示や「続けますか(Y/N)?」という確認処理です。
最低1回はメニューを見せ、そのあとユーザーの入力に応じて2回目以降をくり返す、といったパターンではdo while文が自然です。
while文とif文で作る簡単ミニゲーム
while文+if文の基本パターン
while文の理解を定着させるために、ここではwhile文とif文を組み合わせた簡単なミニゲームを作ってみます。
ミニゲームのようなインタラクティブなプログラムは、ループや条件分岐の練習に最適です。
while文とif文を組み合わせる基本パターンは、次のような構造になります。
- while文で「ゲームを続けるあいだ」くり返す
- その中でif文を使い、「勝ち」「負け」「続行」などの分岐を行う
- 勝ちやあきらめなど、終了条件を満たしたらwhileループを抜ける
このパターンを念頭に置いておくと、さまざまなゲームや対話的なアプリケーションを組み立てやすくなります。
キーボード入力を使ったループ処理の流れ
ミニゲームでは、多くの場合ユーザーのキーボード入力によってゲームが進行します。
C言語で標準入力から値を読み込むには、前のセクションでも登場したscanf関数を使うのが基本です。
キーボード入力を使ったwhileループの典型的な流れは次のようになります。
- ゲーム開始のメッセージを表示する
- while文に入り、「終了条件を満たすまで」ループする
- ループごとに、ユーザーへ入力を促すメッセージを表示する
scanfで入力を読み取る- if文で入力内容を判定し、正解・不正解・終了などの処理を行う
- 正解や終了コマンドが入力されたらbreakでループを抜ける
この流れに沿ってコードを書くことで、「入力→判定→結果表示→次の入力」というゲームのサイクルを実現できます。
正解なら終了・不正解ならくり返す処理
ここでは、1から10の範囲でランダムな整数を1つ決め、それを当てる「数当てゲーム」のミニ版を作ってみます。
正解したらゲーム終了、不正解ならくり返して再挑戦できる構造です。
なお、本記事では説明を簡単にするため、乱数ではなくあらかじめ決めた数(たとえば7)を正解として扱います。
#include <stdio.h>
int main(void) {
int answer = 7; /* 正解の数字(本来は乱数を使うことが多い) */
int guess = 0; /* プレイヤーの入力を受け取る変数 */
int try_count = 0; /* 何回目のチャレンジかを数えるカウンタ */
printf("数当てゲームをはじめます。\n");
printf("1~10の整数を当ててください。\n");
/* 正解するまでくり返すループ */
while (1) {
printf("あなたの予想を入力してください(0であきらめる): ");
if (scanf("%d", &guess) != 1) {
/* 入力が数値として読み取れなかった場合 */
printf("入力エラーです。ゲームを終了します。\n");
break;
}
/* あきらめコマンド: 0が入力されたらゲーム終了 */
if (guess == 0) {
printf("あきらめましたね。正解は%dでした。\n", answer);
break;
}
try_count = try_count + 1; /* チャレンジ回数を1回増やす */
if (guess == answer) {
/* 正解ならメッセージを出してループ終了 */
printf("正解です!%d回目で当てました。\n", try_count);
break;
} else if (guess < answer) {
/* 予想が小さすぎる場合 */
printf("小さすぎます。もっと大きな数です。\n");
} else {
/* 予想が大きすぎる場合 */
printf("大きすぎます。もっと小さな数です。\n");
}
}
printf("ゲームを終了します。\n");
return 0;
}
(実行結果はユーザーの入力によって変わるため、ここでは一例を示します。)
数当てゲームをはじめます。
1~10の整数を当ててください。
あなたの予想を入力してください(0であきらめる): 3
小さすぎます。もっと大きな数です。
あなたの予想を入力してください(0であきらめる): 9
大きすぎます。もっと小さな数です。
あなたの予想を入力してください(0であきらめる): 7
正解です!3回目で当てました。
ゲームを終了します。
このプログラムでは、while (1) と break を組み合わせて複数の終了条件を実現しています。
正解した場合、あきらめた場合、入力エラーの場合の3種類の終了パターンがif文で分岐され、それぞれでbreak;を実行してループを抜けています。
無限ループを避ける終了条件の設計
ミニゲームを作るときにも、無限ループを避けるための終了条件の設計はとても重要です。
上記の数当てゲームでは、次のような工夫で安全性を高めています。
まず、「あきらめる」ための特別な入力(0)を用意することで、プレイヤーが自分の意思でゲームを終了できるようにしています。
これにより、答えがどうしても分からないときでもプログラムを強制終了せずに済みます。
次に、入力エラー時にも確実にループから抜ける処理を入れています。
scanfが失敗した場合は入力バッファがおかしな状態になることがあるため、そのままループを続けると状況が悪化することがあります。
エラー時には早めにループを抜ける設計が安全です。
さらに発展させる場合、試行回数の上限を決めるのも有効です。
たとえば「10回間違えたらゲームオーバー」と決めておけば、何らかのバグで終了条件が満たされない場合でも、一定の回数で強制的にループを抜けることができます。
ミニゲーム作成でwhile文の理解を定着させる
実際にミニゲームを作ってみると、while文に関して次のような点が自然に身につきます。
まず、「ループ条件」と「ゲームの続行条件」が同じ意味を持つことが体感できます。
プレイヤーがまだ遊び続けているあいだはwhileループが動き、やめる決断をしたときにループが終了する、という対応関係です。
また、if文との組み合わせで複雑なルールを実装できることも理解できます。
単純な「正解なら終了」という条件だけでなく、「不正解だがまだチャンスが残っている」「残り回数がゼロになった」など、さまざまな状況に応じてwhileループから抜けるかどうかを決めることができます。
さらに、入出力・変数の更新・終了条件のチェックといった処理の順番を意識しながらコードを書くので、前のセクションで説明した「初期化→判定→処理→更新」の流れが自然と身につきます。
小さなミニゲームでも、while文の実践的な理解には非常に役立ちます。
まとめ
while文は、「条件が真であるあいだ処理をくり返す」ための基本構文であり、C言語プログラミングにおいて欠かせない存在です。
本記事では、カウンタ変数との関係や無限ループを防ぐコツ、break・continueの使い方を通して、while文の動作原理を丁寧に解説しました。
また、for文やdo while文との違い、キーボード入力を利用したミニゲームの例を通じて、用途に応じた使い分けの考え方も紹介しました。
実際に小さなプログラムを書きながら、条件式・更新処理・終了条件を意識してwhile文の感覚を身につけていってください。
