無限ループ – for (;;)/while (true)/while (1)【どちらを使うべきか】


無限ループ

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

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

for (;;) {
   printf("hello");
}
while (true) {
   printf("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 (;;)while (true)と同様、無限ループを表現する記法です。for文は、条件部の式が省略されると条件を真とみなす性質があるため、事実上のfor (;true;)処理によって無限ループが実現されます。

for (;;) 記法の有効性

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

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

無限ループへの最適化

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

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

なおfor (;;)記法が使えず、while (true)への最適化も見込めないような環境では、do {} while (true)で若干のオーバーヘッドを減らすこともできますので覚えておくと良いでしょう。

どちらを使うか

どちらも記法としては明確で優れています。

ただ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:

広告

関連するオススメの記事