無限ループ|while (true)/while (1)/for (;;) の意味や目的|どちらを使うべきか

無限ループ

無限ループ(infinite loop)とはプログラムの処理が永遠と繰り返される状態、またはそのような性質をもったコードのことを言います。

C言語やPython、Java、JavaScript, PHPなど多くのプログラミング言語では、無限ループを実現する方法としてfor (;;)記法やwhile (1)while (true)while True:といった記述を用いる事ができます。

// C/C++
for (;;) {
   printf("hello");
}

// Java
while (true) {
   System.out.println("hello");
}
// JavaScript
while (1) alert("hello");

// Python
while True:
  print("hello")

いずれのコードもループ文内の処理(printf("hello");)が繰り返し実行され続けます。プログラムは終了することなく、同じ処理が永遠に繰り返されることになります。

通常はbreak文やreturn文でループ文を抜け出すためのコードを記述することがほとんどです。

while (1) {
   int c = getchar();
   if (c == '\n') break;
   if (c == EOF)  return -1;
}

このような意図的な無限ループ文は、プログラムの記述を簡潔にする目的や、コードの可読性を高めるための用途として活用されています。

目次

while (true)

while文は条件式が真として評価される間だけループ内の処理を繰り返し実行させるための機能ですが、この条件式に真としての条件(true)を指定することで、無限ループを実現することができます。

while (1) も同じ意味

ちなみにwhile (1)while (true)と同等の意味を持ちます。プログラミング言語の種類によっては0以外の値に対する評価を真とみなすものもあるため、このような記述が可能となっています(PHP, JavaScript等のインタプリタ言語の多くで利用可能)。ですから、while (2)while ("infinite loop")という形で無限ループを表現することも可能ではあります。

なおC言語の世界ではwhile (true)よりもwhile (1)が広く使われています。理由としては、trueリテラルがstdbool.hヘッダファイルに定義されているために簡易的・慣用的に利用できないことや、C89/C90以前の古い規格ではマクロ定数trueが標準では定義されていないこと等が挙げられます。

ちなみに、JavaやSwift言語では条件式の評価基準がBoolean型の真理値であるためtrueの利用が必須となっています。while文が存在しないGo言語では条件部を省略したfor {}という特殊な記法で無限ループを表現しています。

while文を使うことの利点

無限ループを実現する際には、for (;;)よりもこちらのwhileを用いた記法の利用をオススメします。理由は通常の繰り返し文(for文)と区別できるためです。またfor文よりもwhile文のほうが利用頻度が少ないため、バランスが取れるという理由もあります。

実際、一般的なループ処理ではfor文や拡張for文を頻繁に使います。逆にwhile文の場合は特殊なケースで利用されることがほとんどです。無限ループはこの特殊なケースに該当するため、あえてwhile文を使うようにすると、コードの意味や目的が明確化します。そのような区別や使い分けはソースコードの可読性にも良い影響を与えるため非常に有効です。そういう意味ではdo {} while (true);記法の利用も悪くないのですが、冗長的な記述になるためあまり一般的ではありません。

なお古い時代のコンパイラはwhile (真)式を警告することがあるため注意が必要です。

Visual Studio 2005 - コンパイラの警告 (レベル 4) C4127

この警告エラーを根拠にfor (;;)記法を推奨・利用する者たちも多くいます。

for (;;)

for文の条件部を空の状態にしたfor (;;)while (true)と同様、無限ループを表現する記法です。for文は、条件部の式が省略されると条件を真とみなす性質があるため、事実上のfor (;true;)処理によって無限ループが実現されます。

for (;;) 記法の有効性

for (;;)は記法としてはそれなりに優れているように思えます。for文にセミコロンを2つ並べた;;という記述はかなりインパクトがあります。ひと目見ただけで無限ループとわかるある種の記号的な役割を持っています。

ただし、言語によってはC言語スタイルのfor文for (;;)が利用できないようなもの(Swift, Python)もあるため注意が必要です。全ての言語で共通して使える記法ではないというデメリットがあります。

無限ループの最適化

インタプリタ言語によってはfor文の条件式省略時に最適化が働く可能性があります。while (1)while (true)の場合はループ毎に数値リテラルや論理値リテラルに対する評価が発生しますが、for (;;)の場合はそれを回避することが可能になります。

ただし、全てのインタプリタ言語でそれらの最適化が保証されているわけではありません。またwhile (true)を最適化するインタプリタ言語が存在する可能性もあります。

なおfor (;;)記法が使えず、while (true)への最適化も見込めないような環境では、do {} while (true)で初回のオーバーヘッドを減らすこともできますので覚えておくと良いでしょう。もっともbreak文やreturn文による処理の中断や脱出を前提とした無限ループの場合に限ります。

どちらを使うか

どちらも記法としては明確で優れています。両記法の利点については既に述べている通りです。

ただfor文を用いた方法の場合、人によってはfor (; ; )という空白を用いた書き方が使われる場合もあるため、人によって記法にばらつきが生まれやすいという問題もあります。筆者自身は空白無し;;を用いても構わないと思っているのですが、世の中にはそれを良しとしない人たちがいます。

どちらにしても構文チェックツールや入力補完機能との関係もありますから、空白の有り無しは必ずどちらかに統一して利用する必要があります。そういう意味では非常に厄介な記法と言えます。

またC言語スタイルのfor文を採用していない言語も多く存在するため、定型文としての価値は低い記法とも言えそうです(特に for (;;) 記法を知らないプログラマは今後増えていく可能性があります))。

その点、while文を用いた記法はばらつきがなく安定して使えます。

総合的に見ると、while文を用いた記法のほうが問題が少なく安定しており、また様々な言語で統一的に利用できる形式であるという特徴があります。無限ループを実現する場合、今の時代ではwhileを用いたwhile (true)while (1)の利用がベストな選択と言えそうです。

番外編(再帰派)

再帰は余計な関数呼び出しやスタック消費の原因となるため、利用には細心の注意が必要です。デバッグビルド時には、最適化が効かずにスタックオーバーフローが発生することもあるため厄介です。無限ループの繰り返し回数が多くなるような場面では注意が必要です。最近のコンパイル言語では最適化機能が充実してはいますが、仕様上の注意をよく読み用法容量を守って正しく扱いましょう。

番外編(goto文派)

悪名高きgoto文で無限ループや繰り返し処理を実現することも可能です。

int i = 10;
{ begin:
  if (i-- == 0)
    goto end;
  printf("%d", i);
  goto begin;
} end:
広告