同じ処理を繰り返して、行いたい場合、while文、for文を使用することがほとんどでしょう。
しかし、気を付けてほしいのは、C言語におけるwhile文は実行条件が変数同士が等しいときという場合、「=」を一つ書き忘れると、if文の書き忘れより、はるかに致命傷になります。
if文の「=」書き忘れについては、こちらの記事をどうぞ。
C言語のwhile文で等しい判定は「=」ではなく「==」である
C言語において、while文で何かと等しいとき、while文内の処理を実行するというのは、普通にあるでしょう。
例えば、このようなプログラムがあったとします。
short a, b, c; a = 1; b = 1; c = 0; while (a == b) { if (c < 100) { c = c + 1; } else { a = 0; } }
「aとbが同じである間、cが100未満である間、cを1ずつ増やし、cが100になったらaを0にして、while文を抜ける」というプログラムです。
※例として書いているだけですので、cを1ずつ足すのみのプログラムなら、もっと簡単に書けます。
実際に実行すると、cが100になったとき、aを0にしているので、while文の条件aとbが等しいというのを満たさなくなるので、while文を抜けて、次の処理に進みます。
しかし、これをこう書いてしまったら、どうなると思いますか?
short a, b, c; a = 1; b = 1; c = 0; while (a = b) { if (c < 100) { c = c + 1; } else { a = 0; } }
while文の条件のところの「=」を一つ削除しました。
これでも、文法に間違いがないため、コンパイルエラーになりません。
しかし、実行すると、cが100になっても、while文を抜けることができず、無限ループに陥ります。
なぜなら、「a = b」は「aにbの値を代入する」ということなので、aは1になり、while文の評価は0以外で真となるため、while文内の処理が必ず実行され、while文を抜け出すすべがないためです。
if文の場合もそうですが、while文ではたった「=」を一つ忘れただけで、プログラムの暴走につながってしまうのです。
また、aとbの初期値を0、while文内のaの値を1にした場合、while文は実行されずに、次の処理に進みます。
なぜだかわかりますよね?
aにbの値を代入しているので、aは0になります(もともと0ですが)。
したがって、while文の条件は0で偽となるので、while文内の処理は実行されません。
「==」にしておけば、このようなことにはなりません。
プログラムが無限ループに陥ると次の処理に進むことが永久にありませんので、アプリケーションが固まります。組み込みマイコンだと、プログラムのリセットがかかり続けることになるでしょう。
C言語のwhile文では無限ループになる可能性がないかを確認する
先ほどの例文では「aとbが等しい」ということを判定して、繰り返し処理を行うようにプログラムを書いていました。
しかし、「=」を1つ忘れると意図した結果が得られないこともお話ししました。
このようなことを減らしていくには、どうしたらいいかを考えていきます。
次の文をみてください。あやしいところはないですか?
short a, b, c; a = 0; b = 0; c = 0; while (c != 100) { if (a != b) { b = a; c = c + 1; } else { b = a + 1; c = c + 2; } }
「aとbが等しくないとき、cの値を2足して、そうでないとき、cの値を1足して、cが100になるまで繰り返す。」 というプログラムを書いてみました。
残念ながら、これでは無限ループに陥ってしまいます。
おかしいところ、わかりますか?
このまま、cの値を計算してもらえば、わかると思いますが、cの値が100になりません。99のあと、101になります。
while文の条件はcが100でない間、処理をくりかえします。しかし、実際に100にならないので、永遠とwhile文を繰り返してしまいます。
無限ループですね。
これを回避するには、こうするとよいでしょう。
short a, b, c; a = 0; b = 0; c = 0; while (c < 100) { if (a != b) { b = a; c = c + 1; } else { b = a + 1; c = c + 2; } }
どうでしょうか?
while文の条件を「<」にし、cが100未満だったら、while文内の処理を実行するに書き換えました。
こうすると、cが101になったとき、while文を抜けて、次の処理に進むことができます。
cが100になることはありませんが、100より大きい値にすることは可能です。
このようにプログラミングをするにはなるべく、ピンポイントの条件ではなく、範囲で指定しておくと、無限ループを回避することができます。
C言語のwhile文で無限ループに陥ってしまったら
while文で無限ループに陥ってしまい、アプリケーションが固まったり、組み込みマイコンがリセットを繰り返すなら、状況を把握し、解決することが必要です。
しかし、これを見つけるのが非常に苦労します。
Windowsアプリケーションなら、デバッグ機能を用いて、実行を一時停止させることで、その時の値を確認しながら、ステップ実行させることができます。
そのときに、おかしい個所を見つけることができるかもしれません。
一方で、組み込みマイコンの場合は、そういうわけには、なかなかいきません。
あやしいところに、デバッグコードを埋め込んで、それを読み出すとか、リセットしていなかったプログラムとの差を確認して、作ったプログラムに間違いがないのかをチェックしていく必要があります。
あやしい個所をみつけたら、一旦、変数であった箇所を定数にしてみるとか、ループ回数を小さくするとか工夫が必要です。
デバッグコードを埋め込んでも、マイコンがリセットしてしまっては値を読み出すことも困難なので、リセットしないようにハードウェアの設定を変えたりすることも必要になるかもしれません。
ほとんどの場合は、机上のチェックで見つけることができると思いますが、そうでない場合は、デバッグコードを埋め込んで、値を保持できるようにして、リセットしないプログラムにして、その値を見るという方法を取らないといけないこともあります。
いずれにせよ、無限ループに陥ってしまうと、修正するのに時間がかかってしまうので、break文を一時的に追加するとか、ループ処理そのものをコメントアウトして、実行可能かをチェックしていくことで、修正範囲を狭めていく手法を取るほうがよいでしょう。
C言語のwhile文で等しい判定で「=」忘れを防ぐには?
結論からいうと、必ず忘れないようにできるということはありません。
リスクを減らすためには「==」のwhile文を作らない、他の人にもチェックしてもらう、動作テストをするということしかありません。
動作テストを実施すれば、「=」忘れがあると、思うような動作にならないでしょう。
先ほども述べたように、無限ループに陥ってしまうと、見つけるのに非常に苦労することになります。
無限ループにならないように、プログラミングすることはもちろんのこと、万一のときの対処方法を取っておくことが必要不可欠となります。
誰だって、無限ループに陥るように作ろうともは思っていません。しかし、ちょっとしたミスで、そういうことにもなるのがプログラムの世界です。
細かいところ「=」が1つ足りないとか、不等号の向きが逆になっているとかだけで、動作が異なってしまうのです。
プログラムをする以上、これは避けては通れない道ですので、まずは小さいプログラムで経験を積んでいきましょう。