ここでは、繰り返しについて説明します。
繰り返し
プログラムでは繰り返しがよく利用されます。
条件を満たすまで処理を繰り返すということが主な使われ方です。
C言語では、繰り返しのために、「for文」「while文」「do-while文」の3つの文が用意されているので、それらをひとつひとつ説明していきます。
for文
for文を使った例として、1から10の数字を順に表示するソースコードがあります。
#include <stdio.h>
int main(void)
{
int i;
for (i = 1; i <= 10; i++) {
printf("%d\n", i);
}
return 0;
}
for文の書き方は、最初に for と書き、その後にかっこを書きます。
かっこの中は3つに分けられ、ひとつめは「i = 1;」の部分です。
for 文の繰り返しの処理で一番最初に行われる「初期化」と呼ばれる処理をします。最後に必ず ; を忘れないようにしましょう。
ふたつめは、「i <= 10;」の部分です。
繰り返しを続ける条件式を指定します。前の if 文の条件式と同じで、ここが真となる間、繰り返しが続きます。こちらも最後に ; を忘れないようにしましょう。
みっつめは、「i++」の部分です。
一回ごとに繰り返しの処理が終わった際に実行される処理を書きます。今回は、i の値をインクリメント演算子を使って1増やしています。
これらをまとめると、for 文の処理の流れは、最初に、変数i に1を代入します。
そして、次の条件式を確認して、i は 1 で 10 以下であり真であるので、中かっこで囲まれた処理が実行されます。
中かっこの中の処理では、printf関数で i の値を表示します。
中かっこの中の処理が終わったら、かっこのみっつめの部分である i++ が実行され、i の値が1増えます。
i の値を増やしたら、2回めの繰り返しとなり、また条件式をみて、i は 2 で 10以下であり真であるので、中かっこで囲まれた処理が実行されます。
あとは、今までと同じ、中かっこの処理が終わったら i++ が実行されて、3回めの繰り返しが始まるということを繰り返します。
最後は、i++ が実行されてiの値が11となった際に、i は 11 で 10 以下ではないので、条件式は偽となり、繰り返しの処理をしていた for 文は終了します。
これで、1から10の数字が順に表示されます。
i++ のところを自由に書き換えることができるので、 i+=2 のようにすれば奇数が表示されるようになります。
while
for 文が分かれば while 文は単純です。
while 文を使って、数字の桁数を求めてみましょう。
#include <stdio.h>
int main(void)
{
int a, tmp;
int digit;
a = 1000;
tmp = a;
digit = 1;
while (tmp /= 10) {
digit++;
}
printf("%dの桁数は%dです\n", a, digit);
return 0;
}
while 文の書き方は for 文に比べてシンプルです。
初めに、while と書き、その後にかっこを書いてそのかっこの中には、for 文の条件式に当たる部分だけを書くようにしたものが、while 文です。
そのため、初期化をする必要がなかったり、i++ のように、繰り返しの処理ごとに何かしたいということでなければ while 文を使うようにしましょう。
このソースコードでは、1000 という数字の桁数を求めています。正解は4ですが、計算の方法として、結果が0になるまで、1000を10で割り続けています。1000を割り続けると100、10、1、0となり、0の時は条件式が偽となるので while 文が終了します。
while 文と for 文はそれぞれ、置き換えることができます。
例えば、for 文で用意した1から10まで表示するソースコードを while 文で書き換えるとこのようになります。
#include <stdio.h>
int main(void)
{
int i;
i = 1;
while (i <= 10) {
printf("%d\n", i);
i++;
}
return 0;
}
このようになります。
do-while 文
do-while 文は、while 文の条件式による判定を繰り返しの最初ではなく、最後にしたものです。
そのため、while 文で用意した桁数を求めるソースコードをdo-while 文で書き換えたものがこちらです。
#include <stdio.h>
int main(void)
{
int a, tmp;
int digit;
a = 1000;
tmp = a;
digit = 0;
do {
digit++;
} while(tmp /= 10);
printf("%dの桁数は%dです\n", a, digit);
return 0;
}
最初に do から始まって最後に while で終わります。
前のものとの違いは、do-while 文を使っているため、条件式を最後に書き、条件式の判定が最後となり、digit の初期値が0であるということです。
なぜ初期値が0であるのかというと、10で割る回数と桁数は同じなのですが、while 文の時は、10で割った回数に対して1回 digit に1をたす回数が足りないので、digit の初期値を1としていました。
しかし、do-while 文では、最後に条件式の判定をするので、このようなことがなくなり初期値を0としました。
while の後のかっこの最後に ; が必要なので、忘れないようにしましょう。
無限ループ
for 文、while 文、do-while 文では、無限ループと呼ばれる、ずっと繰り返す処理を書くことができます。
その簡単な方法として、条件式をずっと真になるものとするという方法があります。
それぞれ、無限ループとなる書き方を書いておきます。
#include <stdio.h>
int main(void)
{
for (;;) {
}
while (1) {
}
do {
} while(1);
return 0;
}
中かっこの中には、それぞれ好きな処理を書きます。
for 文では、; のみを書き、それ以外のものを書かないようにすることで無限ループとなります。細かいことを言えば、条件式のところのみ書かなければ無限ループとなるので、初期化の部分や繰り返しの最後に行う処理を書いても大丈夫です。
while 文や、do-while 文では、条件式に必ず真となる0以外の数字、一般的に、必ず真としたいという場合には、1を用いるので、1と書くことで無限ループとなります。
なぜ、このような処理の終わらない無限ループを説明したのかというと、いろんなところで使うからです。
しかし、無限ループで永遠に繰り返しが終わらないというのは、不便なため、無限ループを終わらせる方法があります。
無限ループなどで、プログラムが終了しないような場合は、実行中にコントロール(Ctrl)キーとCキーを同時に押します。
そうすると、プログラムを強制終了できます。
break
無限ループなどを終わらせるには、その無限ループの処理内で、「break」というものを実行する必要があります。
例えば、1から10を表示するソースコードを無限ループを使って書き直すとこのようになります。
#include <stdio.h>
int main(void)
{
int i;
for (i = 1; ; i++) {
if (i >= 11) {
break;
}
printf("%d\n", i);
}
return 0;
}
条件式以外のところはそのままで、前回の if 文を使っています。
if 文によって、iの値が11以上になったら「break;」を実行して、for 文の処理を終了するようにしています。ちなみにこのように繰り返し処理を終わらせることを for 文を抜けるやwhile 文を抜けるのように「抜ける」という言葉を使うことがあります。
break が実行されるとその後の処理は実行されなくなるので、直後のprintf関数も実行されないため11とは表示されません。
このように、if 文と break を組み合わせれば、自由にループを抜けることが可能です。
continue
その他にも、ループの中で使えるものに continue というものがあります。
これは、break とは違い、continue が出た後の処理は実行されずに、次の繰り返しに移ります。
例えば、1から10の間の偶数を表示する場合、このように書くことができます。
#include <stdio.h>
int main(void)
{
int i;
for (i = 1; i <= 10; i++) {
if (i % 2 == 1) {
continue;
}
printf("%d\n", i);
}
return 0;
}
2で割ったあまりが1の時、つまり奇数の時には continue を実行し、直後のprintf関数を実行しないようにします。
これで、奇数の時はprintf関数が実行されないため奇数の数字は表示されず、偶数の時は、if 文の条件式が偽となるので、数字が表示されます。
多重ループ
for 文、while 文、do-while 文は、多重ループと呼ばれる、ループの中にループを書くということができます。
例えば、for 文を2つ使って、九九を表示しましょう。
#include <stdio.h>
int main(void)
{
int i;
int j;
for (i = 1; i <= 9; i++) {
for (j = 1; j <= 9; j++) {
printf("%3d", i * j);
}
printf("\n");
}
return 0;
}
printf関数で “%3d” というように書いています。これは、3文字で表示しなさいという意味で、数字が1けたや2けたの場合には、左側へ3文字になるように空白が表示されます。これで、九九の数字を揃えるようにしてあります。
for 文の中に for 文を書きました。
このようにすることで、printf 関数の i * j は「1 * 1」「1 * 2」……「1 * 9」「2 * 1」……「9 * 9」のように計算されていきます。
このような for 文の中に for 文のようなものを「入れこ」や「ネスト」と呼び、さらに for 文 2つのような、二重のループを二重ループと呼びます。
多重ループの内側で、break を使用すると一番内側の繰り返ししか抜けることができません。そのため、内側の繰り返しの break で全ての繰り返しを終わらせたい場合は、フラグと呼ばれる状態を保存しておくための変数を用意して、その変数の値を見て外側の繰り返しでも break をするようにします。
九九において、8の段の「8 * 5」で表示をやめるようにするには、このようにします。
#include <stdio.h>
int main(void)
{
int i;
int j;
int flag = 0;
for (i = 1; i <= 9; i++) {
for (j = 1; j <= 9; j++) {
if (i == 8 && j == 5) {
flag = 1;
break;
}
printf("%3d", i * j);
}
printf("\n");
if (flag) {
break;
}
}
return 0;
}
フラグとして、状態を保存しているのが flag という変数です。
flag には 0 か 0 以外の数字を代入するようにして、変化する前は 0 、変化した後は 0 以外の 1 を代入するようにしています。
そのようにすることで、「if (flag == 1)」の代わりに「if (flag)」のように flag の値を参照して条件分岐することが可能です。
フラグという言葉ですが、世の中のフラグが立ったという言葉は、プログラミングで使われるこのフラグが語源です。
フラグが立ったというのは、状態が変化したということを表しています。
まとめ
この時点では、繰り返しによるメリットが見えにくいですが、これから説明する配列と組み合わせることによって、非常に便利なものになります。
for 文と while 文は必ず覚えておくようにしましょう。