C言語 for文、while文で無限ループを作るときは、3つのことを念頭に置いて作ろう

  • このエントリーをはてなブックマークに追加
Pocket

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つのことについて、お話しました。

無限ループが多ければ、プログラムは重たくなりますし、重大な事故につながる可能性もあるため、そのことを気に留めておいて、いただけたらと思います。

無限ループにはまると、プログラムのバグ修正が難しくなることもあります。

プログラムがリセットされては、原因がつかみにくいですからね。

あなたには、無限ループを作るとき、それが

・本当に必要か?
・必要なら、抜ける条件は確実に実行されるか?
・ループ内の処理は重くないか?

を念頭において、プログラムを作っていってほしいと思います。

  • このエントリーをはてなブックマークに追加

コメントを残す

*