では、新しい考え方を導入してプログラミングの幅を少し広げてみましょう。
ネストという考え方は以前if文の時に説明しました。
何かの命令の{}の内側に別の命令を書くことをネストといいます。
例えば、次のプログラムはネストされています。
<sample program 051-01>
#include <stdio.h> int main(void) { int counter; counter = 0; while(1) { if (counter >= 10) { break; } counter++; } return 0; } |
while文の中にif文が入っています。
もちろん、if文の中にwhile文を書くことも可能です。
では、これを踏まえた上で次へ進みましょう。
まずはネストを使わず、次のプログラムを作ってみてください。
「*」を5個表示するプログラム
<実行結果1>
***** 続行するには何かキーを押してください・・・
ただし、次のようなプログラムではダメです。
<sample program 051-02>
#include <stdio.h> int main(void) { printf("*****\n"); return 0; } |
<実行結果>
***** 続行するには何かキーを押してください・・・
繰り返しを使って作ってください。
解答例です。
<sample program 051-03>
#include <stdio.h> int main(void) { int counter; counter = 0; while (counter < 5) { printf("*"); counter++; } printf("\n"); return 0; } |
<実行結果>
***** 続行するには何かキーを押してください・・・
これまでに説明してきたことを思えば難しくないプログラムです。
では、これを次のように変更してみましょう。
この「*」を5個表示するプログラムを5回繰り返すプログラム
<実行結果>
***** ***** ***** ***** ***** 続行するには何かキーを押してください・・・
解答例の前にヒントです。
while文のネストを使います。
元のプログラムを丸ごと繰り返すのですが、繰り返してはならない箇所もあります。
※例えば変数の宣言は2回も行う必要はありません。
「*」を5個表示するためのカウンタと、このプログラムを5回繰り返すためのカウンタは共有できません。
解答例です。
#include <stdio.h> int main(void) { int counter1; int counter2; counter2 = 0; while (counter2 < 5) { counter1 = 0; while (counter1 < 5) { printf("*"); counter1++; } printf("\n"); counter2++; } return 0; } |
<実行結果>
***** ***** ***** ***** ***** 続行するには何かキーを押してください・・・
ヒントにも書いたように、「*」を5個表示するカウンタ「counter1」と、プログラム全体を繰り返すためのカウンタ「counter2」は別々に用意します。
「counter2」を利用して、元のプログラム全体を5回繰り返します。
分かりにくいと思う方は、以下のように考えてみてはどうでしょう。
<sample program 051-05>
#include <stdio.h> int main(void) { int counter1; int counter2; counter2 = 0; while (counter2 < 5) { /* 元のプログラム */ counter2++; } return 0; } |
※動作しません。
「元のプログラム」部分を言葉に置き換えてみると、単純な「5回繰り返すプログラム」が見えてくると思います。
間違えやすいのが次の例です。
<sample program 051-06>
#include <stdio.h> int main(void) { int counter1; int counter2; counter1 = 0; counter2 = 0; while (counter2 < 5) { while (counter1 < 5) { printf("*"); counter1++; } printf("\n"); counter2++; } return 0; } |
<実行結果>
***** 続行するには何かキーを押してください・・・
どこが違うか分かりますか?
最初にこう書いて悩んだ方もいらっしゃると思います。
カウンタ「counter1」の初期値を繰り返しの外で書いています。
実行結果を見ると「*****」と1回しか表示されていません。
なぜでしょうか???
以前、プログラムは適当に打ってはいけないと書きました。
これは最初から全てのミスを犯してはならないと言う意味ではありません。
プログラムを打った後で、これで大丈夫かどうかチェック(テスト)し、間違っている箇所を訂正していくという考え方です。
上のプログラムでは、実行した結果「*****」しか表示されませんでした。
テストに失敗したわけです。
次は、どこが間違っているかを探し出さなければなりません。
間違いを探すためのツールは色々とありますが、初心者向け講座ですので、地道にチェックしてみましょう。
<sample program 051-06>をトレース(追跡)します。
トレースとは、変数の値の変化を見ながらプログラムの流れを追っていくことです。
※出来れば<sample program 051-06>をメモ帳等にコピーして、プログラムを見ながら読み進めてください。
まず、変数は2つしかありません。
「counter1」と「counter2」です。
この変数の値に注目しながらトレースします。
「counter1」「counter2」ともに初期値は「0」となっています。
最初のwhile文に入った時点で「counter2」のチェックが入ります。
「5」よりも小さいので、問題なく繰り返しの中に入ります。
すぐに「counter1」のチェックが入ります。
これも「5」より小さいので、問題なく繰り返しの中に入ります。
「*」を1つ表示して、「counter1」を「1」だけ増やします。
この時点で「counter1」は「1」ですね。
繰り返しの先頭に戻って「counter1」をチェックします。
まだ「5」より小さいため、繰り返しの中に入ります。
「*」を1つ表示して、「counter1」を「1」増やします。
この時点で「counter1」は「2」ですね。
また繰り返しの先頭に戻って「counter1」をチェックします。
まだ「5」より小さいため、繰り返しの中に入ります。
「*」を1つ表示して、「counter1」を「1」増やします。
この時点で「counter1」は「3」ですね。
また繰り返しの先頭に戻って「counter1」をチェックします。
まだ「5」より小さいため、繰り返しの中に入ります。
「*」を1つ表示して、「counter1」を「1」増やします。
この時点で「counter1」は「4」ですね。
また繰り返しの先頭に戻って「counter1」をチェックします。
まだ「5」より小さいため、繰り返しの中に入ります。
「*」を1つ表示して、「counter1」を「1」増やします。
この時点で「counter1」は「5」ですね。
繰り返しの先頭に戻った時点で、whileの条件が成り立たないため、繰り返しを抜けます。
「改行」を表示して、「counter2」を「1」増やします。
最初のwhile文の先頭に戻り「counter2」をチェックします。
「5」より小さいため、繰り返しの中に入ります。
また内部の繰り返しに入りますが、「counter1」の中身は「5」のままです。
このため、内部のwhile文の条件は成立しません。
繰り返しを行わず、そのまま抜けてしまいます。
これが、不具合の原因です。
内部にあるwhile文は、1回目だけ正確に動作しますが、2回目以降は条件が成り立たず繰り返しは行われません。
ですから、<sample program 051-04>にあるように、内部の繰り返しを行う前に「再度」初期化する必要があるのです。
このように、トレースを行うことで原因を突き止めることが出来ます。
※複雑なプログラムになればなるほど、原因を突き止めることは困難になりますが・・・
なぜ正しく動作しないのか、入門書等にはあまり書かれていません。
ほとんどのプログラムは正しい結果が書かれています。
しかし、実際に1から考えて作ってみるとテキスト等には書かれていない不具合が出ることがあります。
テキスト等のプログラムを丸写しして、「ちゃんと動いた!」=「理解した」ということにはならないことが多いです。
ですから、しっかりと自分の頭で考え、実際にプログラムを作ってみてください。
試行錯誤した上でしっかりと理屈を理解することがスキルアップだと思います。
※もちろん、見ただけでちゃんと「理解」出来る人もいますが・・・
トレースを文字で伝えるのは非常に難しいため、デバッグの方法を書いておきます。
「トレースについて」