C++によるソフトウェア開発において、同じ処理を繰り返し実行する「ループ処理」はプログラムの論理構造を構築する上で欠かせない要素です。
その中でもwhile文は、特定の条件が満たされている間だけ処理を繰り返すという、非常にシンプルかつ強力な制御構造を持っています。
配列の走査やユーザー入力の待機、さらにはゲームループやサーバーの常駐処理など、その応用範囲は多岐にわたります。
しかし、while文は自由度が高い反面、記述ミスによって「意図しない無限ループ」を引き起こし、システムのリソースを枯渇させてしまうリスクも孕んでいます。
また、C++には他にもfor文やdo-while文といったループ構文が存在するため、状況に応じて最適なものを選択する判断力も求められます。
本記事では、while文の基本構文から、実戦で役立つ応用テクニック、そして間違いやすい注意点までをプロフェッショナルの視点で詳しく解説します。
C++におけるwhile文の基本構造
while文は、繰り返し処理を開始する前に「継続条件」を評価する前判定型のループです。
条件式が真(true)である限り、ブロック内の処理が実行され続けます。
基本的な書き方
while文の最も標準的な構文は以下の通りです。
while (条件式) {
// 繰り返し実行したい処理
}
この「条件式」には、最終的にbool型(真偽値)として評価される式を記述します。
C++では数値の0が偽(false)、それ以外が真(true)として扱われる性質がありますが、可読性の観点からはi < 10のように、明示的な比較演算子を用いることが推奨されます。
動作の流れ
while文が実行される際、プログラムはまず条件式を確認します。
ここで条件が偽であれば、一度もブロック内の処理を実行することなくループを終了し、次の命令へと移ります。
条件が真であればブロック内の処理を実行し、末尾まで到達すると再び条件式の評価に戻ります。
このサイクルが条件式の評価結果が偽になるまで繰り返されます。
以下に、カウンター変数を用いた基本的なwhile文のプログラム例を示します。
#include <iostream>
int main() {
int count = 0;
// countが5未満の間、繰り返し実行する
while (count < 5) {
std::cout << "現在のカウント: " << count << std::endl;
// カウンターを更新(これを忘れると無限ループになる)
count++;
}
std::cout << "ループ終了" << std::endl;
return 0;
}
実行結果は以下のようになります。
現在のカウント: 0
現在のカウント: 1
現在のカウント: 2
現在のカウント: 3
現在のカウント: 4
ループ終了
この例では、countが5になった時点で条件式 count < 5 が偽となり、ループを抜けています。
while文とdo-while文の決定的な違い
C++にはwhile文と非常によく似た構文としてdo-while文が存在します。
これら二つの最大の違いは、条件判定を行うタイミングにあります。
後判定型ループ:do-while文
do-while文は、ブロック内の処理を実行した後に条件を評価する後判定型です。
そのため、条件式が最初から偽であっても、最低でも1回は必ず処理が実行されるという特徴があります。
do {
// 処理
} while (条件式);
※do-while文の最後にはセミコロン;が必要である点に注意してください。
どちらを使うべきか
使い分けの基準は、その処理が「条件を確認する前に一度実行する必要があるかどうか」です。
| 構文 | 判定タイミング | 特徴 | 主な用途 |
|---|---|---|---|
| while文 | 実行前(前判定) | 条件を満たさない場合、一度も実行されない | 配列の処理、一般的な反復 |
| do-while文 | 実行後(後判定) | 条件に関わらず最低1回は実行される | ユーザー入力のバリデーション、メニュー表示 |
例えば、ユーザーに数値を入力してもらい、その値が不正であれば再度入力を促すようなケースでは、まず一度入力処理を行わなければ判定ができないため、do-while文が適しています。
ループの流れを制御する:breakとcontinue
ループ処理の中では、特定の条件が発生した際にループを途中で抜けたり、現在の処理をスキップして次の周回へ進んだりしたい場合があります。
そのために使用されるのがbreakとcontinueです。
break文による強制終了
break文が実行されると、その時点でループ全体の処理を終了し、ループの外側へ脱出します。
これは後述する「無限ループ」からの脱出経路としても頻繁に利用されます。
#include <iostream>
int main() {
int i = 0;
while (true) { // 無限ループ
if (i == 3) {
std::cout << "iが3になったので脱出します" << std::endl;
break; // ループを抜ける
}
std::cout << "iの値: " << i << std::endl;
i++;
}
return 0;
}
iの値: 0
iの値: 1
iの値: 2
iが3になったので脱出します
continue文によるスキップ
continue文は、現在のループ回における残りの処理をすべて飛ばし、即座に条件判定のステップへ戻るための命令です。
特定の条件に合致するデータだけを処理対象外にしたい場合に有効です。
#include <iostream>
int main() {
int i = 0;
while (i < 5) {
i++;
if (i == 3) {
// iが3の時だけ表示をスキップ
continue;
}
std::cout << "処理中の数値: " << i << std::endl;
}
return 0;
}
処理中の数値: 1
処理中の数値: 2
処理中の数値: 4
処理中の数値: 5
ここで注意すべき点は、while文でcontinueを使う際、カウンターの更新(i++など)をcontinueよりも前に行うか、あるいはブロックの先頭で行う必要があるという点です。
更新処理をcontinueの後に書いてしまうと、更新が行われず再び同じ条件で判定され続け、予期せぬ無限ループに陥る可能性があります。
無限ループの活用と注意点
「無限ループ」と聞くとバグのように思えるかもしれませんが、プログラミングにおいてはあえて終了条件を持たないループを作成することがあります。
無限ループの書き方
C++で無限ループを作成する場合、while文の条件式に常に真となる値を指定します。
while (true) {
// 永久に繰り返される処理
}
あるいは、古いC言語のスタイルを踏襲してwhile (1)と記述されることもありますが、モダンなC++では論理型のtrueを用いるのが一般的です。
どのような場面で使うのか
無限ループは主に以下のような「イベント駆動型」のプログラムで使用されます。
- GUIアプリケーション:ユーザーが「閉じる」ボタンを押すまで待機し続ける。
- 組み込みシステム:センサーの値を監視し、電源が切れるまで動作し続ける。
- サーバープログラム:クライアントからのリクエストを24時間待ち受ける。
- ゲーム制作:1秒間に数十回の描画と計算を繰り返し行い、終了命令が出るまで継続する。
無限ループの危険性と回避策
意図しない無限ループは、CPUの使用率を100%近くまで上昇させ、システム全体の動作を重くさせたり、フリーズさせたりする原因になります。
特にwhile文の条件式に使う変数の更新を忘れるミスは非常に多く発生します。
プログラムが暴走してしまった場合、多くの開発環境(コンソール)ではCtrl + Cキーを押すことで強制終了させることが可能です。
開発中は、常にループの中に適切な終了条件があるか、あるいはデバッグ用の出力コードを入れて動作状況を確認できるようにしておくことが賢明です。
実践的な応用例:ファイル読み込みとイテレータ
while文は単純なカウントアップ以外にも、データの終端が分からない状況での反復処理に真価を発揮します。
標準ライブラリを使用したファイル入力
C++のstd::ifstreamを使用してファイルから1行ずつ読み込む処理は、while文の典型的な活用例です。
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream file("sample.txt");
std::string line;
if (!file.is_open()) {
std::cerr << "ファイルを開けませんでした" << std::endl;
return 1;
}
// std::getlineが成功(真)を返す間、読み込みを続ける
while (std::getline(file, line)) {
std::cout << "読み込んだ行: " << line << std::endl;
}
file.close();
return 0;
}
このコードでは、std::getline関数が「行を読み込めたかどうか」を戻り値として利用しています。
ファイルの終端(EOF)に達すると読み込みに失敗し、戻り値が偽として評価されるため、自動的にループが終了します。
このように「処理の成否そのものを条件式にする」手法は、C++プログラミングにおいて非常にエレガントで頻用されるパターンです。
ポインタやイテレータの操作
最近のC++ではrange-based for(範囲ベースのfor文)が推奨されますが、より複雑な操作や条件によって進め方を変える必要がある場合には、依然としてwhile文とイテレータの組み合わせが使われます。
#include <iostream>
#include <vector>
int main() {
std::vector<int> nums = {10, 20, 30, 40, 50};
auto it = nums.begin();
while (it != nums.end()) {
if (*it == 30) {
std::cout << "30を見つけたので処理を特殊化します" << std::endl;
} else {
std::cout << "値: " << *it << std::endl;
}
++it; // 次の要素へ
}
return 0;
}
while文を使用する際のベストプラクティス
while文を安全かつ効率的に使用するために、以下のポイントを意識してください。
1. 条件式をシンプルに保つ
条件式が複雑になりすぎると、どのような場合にループが終了するのかが分かりにくくなります。
もし条件が複数の論理演算子(&&や||)で構成される場合は、ループ内でif文とbreakを組み合わせた方が可読性が高まることがあります。
2. ループ変数のスコープを意識する
while文で使用するカウンター変数は、ループの外側で宣言する必要があります。
int i = 0; // ループが終わった後もiは生存し続ける
while (i < 5) {
i++;
}
もし、ループ専用の変数として定義し、ループ外でその値を保持する必要がないのであれば、for文を使用する方が変数のスコープ(有効範囲)を限定できるため安全です。
C++においては、「回数が決まっているならfor文、条件で決まるならwhile文」という使い分けが鉄則です。
3. 浮動小数点数(double/float)を条件式に使わない
以下のようなコードは避けるべきです。
double d = 0.0;
while (d != 1.0) { // 誤差により、ぴったり1.0にならない可能性がある
d += 0.1;
}
コンピュータ内部では浮動小数点数は近似値として扱われるため、計算誤差によってd == 1.0が一度も成立せず、無限ループに陥るリスクがあります。
数値を比較する場合は、誤差を許容する範囲(イプシロン)を指定するか、整数型でカウントを管理するようにしましょう。
C++20以降の進展とループ処理
最新のC++規格(C++20/23など)においてもwhile文の基本構造は変わりませんが、周辺の機能が充実しています。
例えば、std::rangesなどの登場により、データの集合を扱う際にはループを直接書かずに済むケースが増えています。
しかし、低レイヤーの処理やカスタムアルゴリズムの実装において、while文による細かな制御は依然として必須のスキルです。
また、C++17からはif文やswitch文で初期化式が使えるようになりましたが、while文には今のところ初期化式を含める構文は標準化されていません(for文との差別化のため)。
そのため、初期状態のセットアップが必要な複雑なループでは、あえてブロック外に変数を置くwhile文の特性を理解しておくことが重要になります。
まとめ
C++におけるwhile文は、柔軟な条件判定に基づいた繰り返し処理を実現するための基本かつ重要な構文です。
- while文は「前判定」であり、条件が偽なら一度も実行されない。
- do-while文は「後判定」であり、最低でも一度は必ず実行される。
breakとcontinueを駆使することで、ループの流れを緻密に制御できる。- 無限ループは意図的に利用される場面もあるが、暴走を防ぐための設計が不可欠である。
- カウントが必要な処理には
for文、入力待ちやストリーム処理にはwhile文、という使い分けが推奨される。
これらの特性を正しく理解し、適切な場面でwhile文を選択できるようになることで、堅牢で効率的なC++プログラムを記述することが可能になります。
特に条件式の設計ミスによる無限ループは、初心者だけでなく熟練者でも起こしうるミスです。
テスト段階での境界値確認を徹底し、安全なコード設計を心がけましょう。






