C言語のdo-while文は、必ず1回は処理が実行されるという特徴を持つ繰り返し構文です。
while文とよく似ていますが、評価タイミングの違いから、初心者が思わぬバグを生みやすい構文でもあります。
本記事では、do-while文の基本から、while文との違い、よくある落とし穴、安全に使うためのコツまで、図解とサンプルコード付きで詳しく解説します。
do-while文とは?基本構文と特徴
do-while文の基本構文と書き方
まずは、do-while文の見た目とルールを整理します。

do-while文の基本構文は次のようになります。
#include <stdio.h>
int main(void) {
int i = 0; // カウンタ変数を0で初期化
// do-while文の基本的な書き方
do {
// ここに繰り返したい処理を書く
printf("i = %d\n", i);
// 状態を更新しないと無限ループになることに注意
i++;
} while (i < 5); // 条件式の最後にセミコロンを必ず付ける
return 0;
}
上記プログラムの実行結果は次のようになります。
i = 0
i = 1
i = 2
i = 3
i = 4
do-while文の構文には3つの重要ポイントがあります。
1つ目は、必ずdo { ... } while (条件);という順番で書くことです。
while文のようにwhile (条件) { ... }とはならないので、順番を取り違えないようにします。
2つ目は、条件式の末尾にセミコロン;が必要であることです。
ここをよく付け忘れてコンパイルエラーになります。
do-while文専用のルールだと意識してください。
3つ目は、繰り返しの本体となる処理がdo { ... }ブロックに書かれるという点です。
このブロックは少なくとも1回は実行されるため、「必ず1回はやりたい処理」をここに書くのが基本的な使い方になります。
while文との評価タイミングの違い
do-while文とwhile文は、ともに「条件が真なら繰り返す」という仕組みは共通ですが、条件式を評価するタイミングが決定的に異なります。

while文は前判定、do-while文は後判定のループです。
- while文
- ループに入る前に条件をチェックします。
- 最初から条件が偽なら、ブロック内の処理は1回も実行されません。
- do-while文
- ループの後で条件をチェックします。
- 条件が最初から偽であっても、ブロック内の処理は最低1回実行されます。
この違いにより、同じような条件式を書いても、実際の動作が大きく変わることがあります。
特にユーザー入力を1回は受け取りたい場合などでは、do-while文の方が自然に書けることが多いです。
do-while文の実行回数が必ず1回以上になる理由
do-while文が必ず1回は実行されることを、コードで確認してみます。

#include <stdio.h>
int main(void) {
int i = 10; // すでに10なので、条件(i < 5)は最初から偽
do {
printf("このメッセージは必ず1回は表示されます。i = %d\n", i);
i++;
} while (i < 5); // iは最初から10なので、ここは最初から偽
printf("ループ終了後のi = %d\n", i);
return 0;
}
実行結果は次のようになります。
このメッセージは必ず1回は表示されます。i = 10
ループ終了後のi = 11
条件(i < 5)は、最初から偽です。
しかし、実際には1回だけメッセージが表示されています。
これは、条件判定が「処理ブロックの後」に行われるためです。
この性質は、次のような場面で特に役立ちます。
- 少なくとも1回はメニューを表示したいとき
- ユーザーからの入力を必ず1回は受け取りたいとき
- ファイルからの読み込みなどを「1回やってみてから」続行するか決めたいとき
while文との違いを具体例で理解
while文との比較コード例
while文とdo-while文の違いを、同じような処理内容で比較してみます。

while文の例
#include <stdio.h>
int main(void) {
int value;
printf("0以外の数値を入力してください(0で終了します)。\n");
scanf("%d", &value);
// while文(前判定)の例
while (value != 0) {
printf("あなたが入力した値は %d です。\n", value);
printf("もう一度入力してください(0で終了します)。\n");
scanf("%d", &value);
}
printf("プログラムを終了します。\n");
return 0;
}
ユーザーが最初に0を入力した場合、while (value != 0)は最初から偽になり、ループ本体は1回も実行されません。
do-while文の例
同じ処理をdo-while文で書き直した例です。
#include <stdio.h>
int main(void) {
int value;
// do-while文(後判定)の例
do {
printf("0以外の数値を入力してください(0で終了します)。\n");
scanf("%d", &value);
if (value != 0) { // 0以外のときだけ表示
printf("あなたが入力した値は %d です。\n", value);
}
} while (value != 0); // 入力値が0ならループ終了
printf("プログラムを終了します。\n");
return 0;
}
この場合は、ユーザーが最初に0を入力しても、一度は必ずプロンプトが表示されるため、インタラクティブな処理を書くときに自然な流れになります。
do-while文が向いているケース
do-while文が力を発揮するのは、「1回は必ずやりたい処理」があるループです。
代表的なケースを整理します。

1つ目は、メニューを表示して選択させる処理です。
メニュー画面は最低でも1回は見せる必要があり、入力内容によって繰り返し表示するかが決まるため、do-while文と相性が良いです。
2つ目は、再試行をユーザーに尋ねる処理です。
例えば「もう一度やりますか?(y/n)」のような確認を、少なくとも1回は行う場面では、do-while文を用いると読みやすくなります。
3つ目は、「1回試してから」継続するかどうかを判断する処理です。
ファイルオープンや通信処理など、まず試行してから結果に応じてループを続行するようなケースに向いています。
while文の方が適しているケースとの見分け方
一方で、必ずしもdo-while文が最適とは限りません。
while文の方が自然になる場面も多くあります。

代表的なケースを整理した表です。
| 観点 | do-while文が向くケース | while文が向くケース |
|---|---|---|
| 初回実行 | 最低1回は実行したい | 0回でも問題ない |
| 典型例 | 入力受付、メニュー表示 | 範囲内ループ、配列処理 |
| 読みやすさ | 「1回やってから決める」流れ | 「やる前に条件を確認」する流れ |
| 初期状態 | 条件式に依存せず開始したい | 条件を満たすときだけ開始したい |
コードを書いていて「この処理は0回でもよいのか?」と自問したとき、0回もあり得るならwhile文、必ず1回は行わないとおかしいならdo-while文、と判断すると整理しやすくなります。
初心者がハマるdo-while文の落とし穴
セミコロン(;)の付け忘れによるコンパイルエラー
do-while文特有の落とし穴として条件式の末尾セミコロン;の付け忘れがあります。

次のようなコードは、典型的なミスです。
#include <stdio.h>
int main(void) {
int i = 0;
do {
printf("i = %d\n", i);
i++;
} while (i < 5) // ← セミコロンを付け忘れた例
return 0;
}
このように書くと、多くのコンパイラで「構文エラー」「expected ‘;’」のようなメッセージが出ます。
正しくは次のように;を付けます。
} while (i < 5); // ← 行末のセミコロンを忘れない
while文には;が付かないため、while文とdo-while文の混同からこのミスが発生しやすくなります。
「do-whileのwhileにはセミコロン」と、セットで覚えておくことをおすすめします。
条件式の書き方ミスによる無限ループ
do-while文に限った話ではありませんが、条件式のミスは無限ループの大きな原因です。
do-while文は「1回は必ず実行される」ため、その1回目で誤った状態になり、そのまま無限ループに入るケースもあります。

次のコードは、条件式の比較演算子を誤って書いた例です。
#include <stdio.h>
int main(void) {
int i = 0;
do {
printf("i = %d\n", i);
i++;
// 本当は i < 5 と書きたかった
} while (i = 5); // ← 比較ではなく代入になっているミス
return 0;
}
このコードではi = 5が「代入」になっており、条件式としては「5(真)」と評価されます。
そのため常に真になり無限ループが発生します。
本来書きたかったのは次のような比較です。
} while (i < 5); // 正しくは比較演算子<
条件式を書くときは、代入演算子=と比較演算子==や<などを混同しないことが重要です。
変数の初期化忘れ・更新忘れ
ループに使う変数の初期化忘れや更新忘れも、do-while文でよく起きるミスです。

初期化忘れの例を見てみましょう。
#include <stdio.h>
int main(void) {
int i; // 初期化していない
do {
printf("i = %d\n", i);
i++;
} while (i < 5);
return 0;
}
変数iを初期化していないため、未定義の値からスタートしてしまいます。
これは動作が予測できない危険なバグです。
また、更新忘れの例も典型的です。
#include <stdio.h>
int main(void) {
int i = 0;
do {
printf("i = %d\n", i);
// i++ を書き忘れた
} while (i < 5); // i がずっと0のままなので、条件(i < 5)は永遠に真
return 0;
}
このコードでは、iが更新されないため、i < 5がずっと真となり、無限ループになります。
ループを書くときは、「初期化・条件・更新」の3つがそろっているかを、毎回意識して確認すると安全です。
ブロック範囲とローカル変数のスコープの勘違い
do-while文の中で宣言した変数は、そのブロック{ }の中でのみ有効です。
このスコープの概念を勘違いすると、予期せぬコンパイルエラーやバグにつながります。

次のコードを見てください。
#include <stdio.h>
int main(void) {
do {
int x = 10; // doブロック内のローカル変数
printf("x = %d\n", x);
} while (0);
// ここでxを使おうとするとエラー
// printf("外側でのx = %d\n", x); // コンパイルエラー
return 0;
}
xはdo { ... }ブロック内だけで有効な変数です。
そのため、外側でxを使おうとすると、未宣言の変数としてエラーになります。
スコープのルールとして、「宣言したブロックを出たら、その変数は使えない」と覚えておくとよいです。
これはdo-while文に限らず、if文やfor文のブロックでも同様です。
フラグ変数の使い方を誤った条件分岐
do-while文では、「続けるかどうか」をフラグ変数で管理することがあります。
このとき、フラグの値や条件式を誤ると、意図しないループになります。

次のコードは、フラグ変数の扱いを誤った例です。
#include <stdio.h>
int main(void) {
int continue_flag = 1; // 1なら続行、0なら終了のつもり
do {
int input;
printf("数値を入力してください(0で終了): ");
scanf("%d", &input);
if (input == 0) {
continue_flag = 0; // 終了したい
}
// ここで本来は、inputに応じた処理を行う
} while (continue_flag = 1); // ← 間違い: 代入にしてしまっている
printf("終了しました。\n");
return 0;
}
ここではcontinue_flag = 1が代入になっており、条件式としては常に真です。
そのため、フラグを0にしてもループが終わらないというバグが生じます。
正しくは比較演算子を使って次のように書きます。
} while (continue_flag == 1); // 比較にする
もしくは、フラグを0か1で扱うなら、while (continue_flag)のように「0以外なら真」とみなして書く方がシンプルな場合もあります。
do-while文を安全に使うためのコツ
無限ループを防ぐ条件設計のポイント
do-while文を安全に使うためには、無限ループを避ける条件設計が重要です。

条件設計のポイントを、文章で整理します。
まず、開始状態として、ループに入る前に変数をどの値にしておくかを明確にします。
例えばiを0から始めるのか、1から始めるのか、あるいはユーザー入力で決まるのかを意識します。
次に、更新として、ループの本体で変数をどのように変化させるのかを考えます。
加算、減算、入力の再取得など、状態が必ず変わる処理を入れることが重要です。
最後に、終了条件として、「どの状態になったらループをやめるのか」を条件式で正しく表現します。
条件が「狭すぎたり」「広すぎたり」すると、期待通りに終了しません。
この3つが論理的につながっているかを、紙に書き出して確認する習慣をつけると、無限ループをかなり防げます。
デバッグしやすい書き方とprintfによる確認
do-while文の挙動がわからなくなったときは、printfによるログ出力が非常に役立ちます。

次のように、ループ内で変数の値や分岐の状態を出力してみます。
#include <stdio.h>
int main(void) {
int i = 0;
do {
printf("[DEBUG] ループ開始時のi = %d\n", i); // デバッグ用出力
// 実際の処理
printf("処理中... i = %d\n", i);
i++; // 状態更新
printf("[DEBUG] 更新後のi = %d\n", i); // デバッグ用出力
} while (i < 3);
printf("ループ終了時のi = %d\n", i);
return 0;
}
[DEBUG] ループ開始時のi = 0
処理中... i = 0
[DEBUG] 更新後のi = 1
[DEBUG] ループ開始時のi = 1
処理中... i = 1
[DEBUG] 更新後のi = 2
[DEBUG] ループ開始時のi = 2
処理中... i = 2
[DEBUG] 更新後のi = 3
ループ終了時のi = 3
このように、ループの各段階で値を確認することで、「どこでおかしくなっているのか」を特定しやすくなります。
デバッグ用の[DEBUG]というプレフィックスを付けておくと、後でまとめて削除するときにも探しやすくなります。
while文と使い分けるための判断基準
最後に、do-while文とwhile文をどう使い分けるかの判断基準を整理します。

判断の軸として、次の2点を意識するとよいです。
1つ目は、「この処理は0回でも矛盾しないか?」という問いです。
0回でも問題ないならwhile文が自然であり、0回だとプログラムの意味が崩れるならdo-while文が適しています。
2つ目は、「読み手にとってわかりやすいか?」という視点です。
同じ動作をするコードでも、前判定で書いた方が直感的に理解しやすい場面と、後判定で書いた方が自然な場面があります。
とくにチーム開発では、他人が読んだときの分かりやすさも考慮することが大切です。
最終的には、「少なくとも1回実行する必要があるときにだけdo-whileを使う」というルールにしておくと、構造的に混乱しにくくなります。
まとめ
do-while文は、必ず1回は処理を実行する後判定ループとして、while文とは異なる役割を持ちます。
メニュー表示やユーザー入力のように「最低1回は実行したい処理」に向いており、うまく使えばコードを自然で読みやすくできます。
一方で、セミコロンの付け忘れや条件式のミスによる無限ループなど、初心者がハマりやすいポイントも多い構文です。
この記事で紹介した、初期化・更新・条件の3点チェックと、printfを使ったデバッグを意識しながら、while文との違いを踏まえて適切に使い分けていけば、do-while文を安心して活用できるようになります。
