C言語に限らず、繰り返し回数がプログラムを書く段階では条件がわからず、プログラムを実行するごとに変わる可能性がある、ということがあると思います。
そのとき、for文かwhile文で無限ループを作って、抜ける条件を無限ループ内に書くことになります。
もし、無限ループを作るとなったら、プログラムが暴走しないように次の3つのことを念頭において置きましょう。
本当に無限ループでなければいけないか?
そもそも、本当に無限ループでないと、プログラムの実現が無理なのか?
ということです。無限ループのリスクはおわかりと思いますが、そのループから永久に抜けられない状態になることです。
そうなると、プログラムが暴走する、固まるなどで、重大な事故等につながるかもしれません。
事故にならなくとも、想定した動作にならないなんてことも十分にありえます。
そうならないためにも、無限ループを使わなくて済むのなら、使わないようにプログラムの設計を見直してみましょう。
特殊ですが、組み込みマイコンの場合、メインルーチンは無限ループになっています。
そうしなければ、刻一刻と変わる入力データに対する動作を実現することができないですから。
通常、プログラムを1から作ることはないので、メインルーチンが無限ループになっているということを、意識する必要はありません。
しかし、次の周期でどうなるとか、実行順がこうだからということは、イメージしながら作っていく必要はあります。
必ず、無限ループを抜けることができるのか?
とは言っても、無限ループを使わないと、実現できないプログラムは数多くあるでしょう。
その場合は、必ず、無限ループを抜ける処理があることが前提です。先ほどいった、組み込みマイコンのメインルーチンは除きます。
無限ループを抜ける条件を確実に記述し、その条件は絶対に満たすことを確認しましょう。
組み込みマイコンの場合は無限ループから抜け出せないと、たいていの場合、マイコンはリセットしてします。
その無限ループが、必ず実行されるのなら、ずっとマイコンリセットをし続けることになってしまいます。
怖いですよね。エレベータが走行中に、マイコンがリセットして、急停止したら。
車のスピードがブレーキを踏んでも落ちなかったら。
for文やwhile文の中で、確実にbreak文を実行できる条件を書きましょう。
例えば、こんな感じで。
int i; int array[10000]; i = 0; while (1) { array[i] = i; i++; if (i >= 10000) { break; } }
例のプログラムでは i は 通常、10000以上にはなりません。しかし、ここは10000以上でbreakするようにしておくほうが無難です。
万が一(この例ではないでしょうが)、(i == 10000) としていて、i が 9999 から 10001 になったら、条件を満たさなくなりますよね。
そうなると、無限ループにから抜け出せなくなりますし、配列の要素番号に範囲外の数値でアクセスすることになるため、プログラムが暴走する可能性が出てきます。
この例では、iがオーバーフローして、0からカウントアップし直すので、(i == 10000) の条件を満たすことは可能です。ただし、配列のアクセスエラーが起こることには変わりありません。
また、注意してほしいのは、break文は一番内側のfor文やwhile文のみ抜けるということです。
for文やwhile文が入れ子になっていると、break文を書いたのに、意図した通りに無限ループを、抜け出せないということが起こるかもしれません。
break文の記述する位置を注意しましょう。
関数内の記述なら、無限ループ以降に処理がない場合は、break文ではなく、return文で関数を抜けてしまうのも1つの方法です。
<この場合は、外側のwhile文に対して、break文がないため、無限ループになってしまう。> int i; int array[10000]; i = 0; while (1) { while (1) { array[i] = i; i++; if (i >= 10000) { break; } } } <この場合は、外側のwhile文に対しても同時に抜けたければ、 外側のwhile文に対するbreak文も追加する必要がある。> int i; int array[10000]; char flag; i = 0; flag = 0; while (1) { while (1) { array[i] = i; i++; if (i >= 10000) { flag = 1; break; } } if (flag) { break; } } <関数内での処理で、以降に処理がない場合、return文にしておくとよい。> int i; int array[10000]; i = 0; while (1) { while (1) { array[i] = i; i++; if (i >= 10000) { return; } } }
例ではwhile文で書きましたが、for文でも同じです。
無限ループでは、繰り返し処理実行ごとに、ループを抜ける条件をチェックするように、プログラムを書いてください。
無限ループ内の処理が重すぎることはないか?
無限ループということは、同じ処理を何回も繰り返すってことですよね。
ということは、その処理はできるだけ、軽くするべきです。
あまりに重たい処理を何回も繰り返すと、組み込みマイコンの場合は、最悪、マイコンがリセットします。Windowsアプリ系だと、キーボードやマウスの入力を受け付けない、なんてことになるかもしれません。
プログラム全般に言えることですが、各ブロックごとの処理は、できる限り軽くしなくては、プログラムのパフォーマンスが維持できません。
無限ループの中に、また数多くの繰り返し処理があって、さらに、その中に繰り返し処理があるなんていうのは、避けるべきです。
そうしなくても、きっと別の方法で実現することができるはずです。
どうしても、できないのなら、それ以外の処理を軽くするしか、全体のパフォーマンスを維持することができません。
繰り返して、実行させたいからこそ、軽くしておかなければ、機能として問題はなくても、運用面で問題がでてしまうかもしれません。
あなた自身が、動きの遅い(重い)アプリケーションを使いたいと思いますか?
おそらく、思わないでしょう。あなたが、思わないことは、ユーザーも思わないということを、心にとめておいてください。
まとめ
無限ループを作るときは、常に念頭においておくべき3つのことについて、お話しました。
無限ループが多ければ、プログラムは重たくなりますし、重大な事故につながる可能性もあるため、そのことを気に留めておいて、いただけたらと思います。
無限ループにはまると、プログラムのバグ修正が難しくなることもあります。
プログラムがリセットされては、原因がつかみにくいですからね。
あなたには、無限ループを作るとき、それが
・本当に必要か?
・必要なら、抜ける条件は確実に実行されるか?
・ループ内の処理は重くないか?
を念頭において、プログラムを作っていってほしいと思います。