ここでは while を使った繰り返し文をおこなってみましょう。
while文を使うことで、同じ処理や似た処理を繰り返しておこなわせることができます。
繰り返しのことをループと呼びます。
動かしてみよう
早速サンプルコードを動かしてみましょう。
入力値の階乗を計算するサンプルを用意しました。
(階乗:自分以下の値を全て掛け合わせたもの。4の階乗の場合、4!=4×3×2×1=24)
入力欄に数字をいれて、実行ボタンを押してください。
実行できましたか?
では続けて解説をおこないます。
解説
while文の基本構文は下記です。
while( 条件式 ){ 処理 }
条件式が成立している間、処理を何回もおこないます。
inputに 3 を入れたと仮定して、サンプルコードの動きを追ってみましょう。
[1週目]
while( input > 0 ) /* input = 3 → 条件式成立 */ { /* output = 1(初期値) */ output *= input; /* output = 1 × 3 = 3 */ input--; /* input = 3 - 1 = 2 */ }
[2週目]
while( input > 0 ) /* input = 2 → 条件式成立 */ { /* output = 3 */ output *= input; /* output = 3 × 2 = 6 */ input--; /* input = 2 - 1 = 1 */ }
[3週目]
while( input > 0 ) /* input = 1 → 条件式成立 */ { /* output = 6 */ output *= input; /* output = 6 × 1 = 6 */ input--; /* input = 1 - 1 = 0 */ }
[4週目]
while( input > 0 ) /* input = 0 → 条件式が不成立のためwhile終了 */ { output *= input; input--; } printf("%d です。", output);/* 3週目終了時点での output は 6。これが出力される。 */
while文を用いることで、3×2×1 と順次計算をおこなっていることがわかると思います。
do〜while
while文の兄弟にdo〜while文がいます。
基本構文は下記です。
do{ 処理 }while( 条件式 );
while文との違いは、条件式と処理のどちらを先にやるかです。
while文は先に条件判定をおこないますが、do〜whileでは先に処理をおこないます。
それにより挙動に差がでます。
先のサンプルコードをdo〜whileに置き換えたものを用意しました。
入力値をいれて実行してみましょう。
実行できましたか。
では、先ほどと同様にinputに 3 を入れたと仮定して、サンプルコードの動きを追ってみましょう。
[1週目]
do /* 最初は判定無し。{}内の処理に進む */ { /* output = 1(初期値) */ output *= input; /* output = 1 × 3 = 3 */ input--; /* input = 3 - 1 = 2 */ }while( input > 0 ); /* input = 2 → 条件式成立。先頭の do に戻る。 */
[2週目]
do { /* output = 3 */ output *= input; /* output = 3 × 2 = 6 */ input--; /* input = 2 - 1 = 1 */ }while( input > 0 ); /* input = 1 → 条件式成立。先頭の do に戻る。 */
[3週目]
do { /* output = 6 */ output *= input; /* output = 6 × 1 = 6 */ input--; /* input = 1 - 1 = 0 */ }while( input > 0 ); /* input = 0 → 条件式が不成立のため、do〜while終了。このまま下に抜ける。 */ printf("%d です。", output);/* 3週目終了時点での output は 6。これが出力される。 */
whileでは4週目の先頭で処理が終わりましたが、do〜whileでは3週目の最後で処理が終わりました。
また処理順序に多少の差はあれども、両者とも 3! = 3×2×1 = 6 を算出できました。
が、実はここに1つ不具合が存在します。
注意点にて述べます。
注意点
while と do〜while は違う人。
while と do〜while は兄弟だと言いましたが、つまり違う人です。
同じ人だと思ったら怪我をします。
先のサンプルでは処理部分に完全に同じ処理を用いて、while でも do〜while でも得られる結果が同じことを確認しました。
ですが、実は同じでない時があります。
お手数ですがちょっとページを上に戻って、それぞれのサンプルコードに 0 を入れた時の結果の確認をしてみてください。
どうでしたか。
while では 0! = 1 と正しい結果が得られていたにも関わらず、do〜while では 0! = 0 と誤った結果が得られてしまったと思います。(数学的に 0! = 1 と定義されているので、whileの結果が正解です。)
3!は正しかったのに0!は違うとか、ホラーですね。仕事でこのミスをしたら製品によってはリコールです。その意味でもホラーですよ。。
では input = 0 を入力した時に何が起きたか見てみましょう。
while文
while( input > 0 ) /* input = 0 → 条件式が不成立のためwhile終了 */ { output *= input; input--; } printf("%d です。", output);/* output として 1 が出力される。(初期値のまま) */
do〜while文
do /* 最初は判定無し。{}内の処理に進む */ { /* output = 1(初期値) */ output *= input; /* output = 1 × 0 = 0 */ input--; /* input = 0 - 1 = -1 */ }while( input > 0 ); /* input = -1 → 条件式が不成立のため、do〜while終了。このまま下に抜ける。 */ printf("%d です。", output);/* output として 0 が出力される。 */
do〜whileはその性質上、最初に一回処理をやるんでしたね。
そのため入力値が 0 だった場合に、やらなくていい output = 1×0 = 0 をやってしまい、誤った結果を出しています。
プログラム設計時には、while と do〜while のどちらを使うか、一度熟考が必要です。
(do〜whileが悪いわけではないです。今回の例では while が適切だったということです。)
無限ループ
while文でやりがちなミスとして、抜けられないループ(無限ループ)を作ってしまう場合があります。
いざプログラムを書いて実行!あれ、うんともすんとも言わないな。
それが無限ループです。
事例をみてみましょう。
while( input > 0 ) { output *= input; }
input−− の入れ忘れですね。while から出れません。
次に多少気づきにくい事例もみてみましょう。(サンプルとはやや式を変えています。)
unsigned int input = 3; while( input >= 0 ) { output *= input; input--; }
input の型が符号無しであり負値とならないため、input >= 0 となりません。
コーディングした人は input−− で対応してるつもりでも、input = 0 – 1 = 65535 となります。
(この現象をアンダーフローといいました。変数と型参照。)
input を符号有りにするなどの対応が必要です。
まとめ
まとめです。
- while で処理が繰り返せます。
- do〜while でも処理が繰り返せます。
- while と do〜while は兄弟だけど他人。ちょっと動きが違います。
- 無限ループに気をつけましょう。
以上です。
ではまた。