次は、繰り返し文です。3種類ありますが、どれも名前のとおり、繰り返し処理を行うための文です。繰り返し処理のことをループと呼ぶこともあります。
while文は次のように書きます。
while (制御式) 文
while文では、制御式の値が0になるまで、そのあとに続く文を繰り返し評価します。制御式の評価は、最初に行われます。つまり、次のような順序で評価されることになります。
最初にwhile文が評価されるときでも、制御式の値が0ならば文は実行されません。したがって1回も文が実行されないということがあり得ます。
do while文は次のように書きます。
do 文 while (制御式) ;
while文の書式と比べてみると、「while (制御式)」「文」との順序が入れ替わり、先頭に“do”、最後に“;”が追加されています。評価の順序もwhile文とは入れ替わっています。
評価の順序は、while文とdo while文の書き方の順序と一致しています。while文は制御式を書いて、そのあとに文を書きます。これがdo while文では逆になっているというわけです。
int n =0; // n < 3 の間、nを1ずつ加算(インクリメント)します。 while (n < 3) n++; printf("%d\n", n); // 3 // 0 < n の間、nを1ずつ減算(デクリメント)します。 do n--; while (0 < n); printf("%d\n", n); // 0
この例では「n++;」や「n--;」といった文を使いましたが、代わりにブロックを使うこともできます。
while文とdo while文では、条件が成立しているときに実行されている文の他には制御式しか記述できませんでしたが、for文ではその他に2つの式を書くことができます。書き方は次のようになっています。
for (式1; 制御式2; 式3) 文
評価される順序は次のようになっています。
制御式の評価方法は、while文やdo while文と同じです。これらと違うのは、for文が開始されたときに最初だけ評価される式1があることと、文が実行されたあとに実行される式3があることです。
1から10までの和を求める計算は次のように書けます。
#include <stdio.h> #include <stdlib.h> int main(void) { int n = 0; int i; for (i = 1; i <= 10; i++) { n += i; } printf("%d\n", n); // 55 return EXIT_SUCCESS; }
式1や式3の値は、文を実行するかどうかには影響しません。あくまでも制御式2の値が0かそうではないかで、文を実行するかどうかが決まります。
式1、制御式2、式3は、いずれも省略することができます。式1と式3は、省略すると評価のタイミングで何も行いません。制御式2を省略すると、そこに0以外の値が書いてある場合と同じ動作になります。つまり、制御式2の評価結果によるfor文終了はなくなります。
なお、C99からは式1の代わりに、宣言を書くことができるようになりました。これを利用すると、上記のサンプルコードは次のように書き直すことができます。
#include <stdio.h> #include <stdlib.h> int main(void) { int n = 0; for (int i = 1; i <= 10; i++) { n += i; } printf("%d\n", n); // 55 return EXIT_SUCCESS; }
for文で宣言された変数は、そのfor文の文の中でのみ有効です。その外側では利用できません。
なお、C99のプログラムをコンパイルするには、gccではコンパイルオプションに、「-std=c99」をつける必要があります。Pleiadesでは次のようにします。
分岐文は、実行の流れを制御します。プログラムの実行は上から下へ進みますが、分岐文に出会うと別の場所へ移動します。
continue文とbreak文は、繰り返し処理(while文、do while文、for文)の中で使われます。
繰り返し処理の文が複文(ブロック)になっていて、その中でcontinue文が現れると、ブロックの最後の位置に分岐します。break文が現れると、繰り返し処理の外側に分岐します。
continue文はあくまでもブロックの最後に分岐するだけですので、繰り返し処理を抜けるわけではありません。繰り返しはcontinue(継続)します。break文は繰り返し処理を抜けます。この違いを覚えて下さい。
また、continue文とbreak文は、その文が含まれるもっとも内側の繰り返し処理にのみ作用します。繰り返し処理が入れ子になっていても、分岐文が含まれているもっとも内側の繰り返し処理の最後に分岐します。また、分岐文がif文などの複文の中にあったとしても関係はなく、もっとも内側の繰り返し処理の最後に分岐します。
int n = 0; for (int i = 1; i <= 10; i++) { // iが3で割り切れるときだけ10を足します。 if (i % 3 == 0) { n += 10; continue; // if文の最後ではなく、for文の内側の最後の位置(*)に分岐 } // iが5で割り切れるときは20を足して繰り返し処理を終了 if (i % 5 == 0) { n += 20; break; // if文の最後ではなく、for文の外側(**)に分岐 } n += i; // (*) } // (**) printf("%d\n", n); // 37( = 1 + 2 + 10 + 4 + 20)
break文は、あとで説明するswitch文でも利用できます。
goto文も分岐に使いますが、繰り返し処理だけではなく、どこでも利用できます。goto文の分岐先は、指定するラベルの位置になります。goto文とラベルの書き方は次のようになります。
// goto goto ラベル名; // ラベル(ラベル付き文) ラベル名: 文
goto文より前にラベルがあってもかまいません。ただし、goto文で分岐できるのは1つの関数(*2)の中だけです。それより外へは分岐できません。
また、1つの関数の中に同じ名前のラベルが2つあってはいけません。
(*2) 関数については今後の記事で解説する予定です。
ラベル名は識別子として利用できる文字が使えます。式ではありませんので、演算子や予約語、定数などは利用できません。
ラベル自体はプログラムの実行に影響を与えません。つまり、ラベルの前後に文があり、直前の文を評価したあとは、ラベルがないときと同じように次の文を実行していきます。
int n = 0; for (int i = 1; i <= 10; i++) { printf("i:%d\n", i); // iが3で割り切れるときだけ10を足します。 if (i % 3 == 0) { n += 10; continue; // if文の最後ではなく、for文の内側の最後の位置(*)に分岐 } for (int j = 1; j <= 5; j++) { printf("n:%d\n", n); // nが100より大きくなったら、すべての繰り返し処理を終了 if (n > 100) { goto LOOP_I; // ラベル LOOP_I に分岐 } n += i * 10; } // (*) } LOOP_I: printf("%d\n", n);
このプログラムを動作させると、次のようになります。iが2でnが110となったときに、「LOOP_I:」のラベルへ処理がジャンプしていることが分かります。
i:1 n:0 n:10 n:20 n:30 n:40 i:2 n:50 n:70 n:90 n:110 110
Cは構造化プログラミング言語の1つです。構造化プログラミングとは、ダイクストラにより提唱されたもので、Wikipediaの「構造化プログラミング」によると、「1つの入り口と1つの出口を持つようなプログラムは、「順次・反復・分岐」の3つの基本的な論理構造によって記述できる 」という構造化定理がベースとなっています。構造化プログラミングでは、goto文を使わなくてもプログラムは書けるので、理論的には使用禁止としても良いのですが、全面禁止となると不便な場面もあります。そんなときは、処理の流れを無視したgoto文とならないようにし、順次・反復・分岐によりまとめられた処理の流れを壊さないように気をつけて使うようにしましょう。goto文を使っていいか判断が難しいと思う場合は、割り切って使わないで実装する方法を考えてみましょう。そうやって経験を積むと、どんなときにgoto文を使った方がいいのか分かるようになるはずです。
switch文は、if文とgoto文を組み合わせたようなもので、次のように書きます。
switch (制御式) { case ラベル: 文 default: 文 }
制御式は整数型の値を持たなければなりません。その整数型の値は整数拡張されてから評価されます。
caseラベルは、整数定数式である必要があります。その式は、整数拡張された制御式の型に変換されます。これはgoto文で使用したラベルとは異なります。
最後に、制御式の値と一致するラベルに分岐します。もし一致するラベルが見つからない場合はdefaultラベルに分岐します。defaultラベルは省略できます。一致するラベルが見つからず、defaultラベルもない場合には、どの文も実行されずにswitch文を抜けます。
ここで注意したいのは、あるラベルに分岐して、ラベルに続く文を実行したあと、次のラベルの場所まで来たとしても、ラベルがないときと同じように次の文を実行していきます。これはgoto文のラベルと同じです。
もし途中でswitch文の処理を終了したいときは、break文を利用します。switch文で使用されるbreak文は、繰り返し処理で利用したときと同じように、switch文を抜けた直後の位置へ分岐します。
break文について動作を確認するために、次のようなコードを動かしてみましょう。最後のprintf関数の出力は3となりますから、コメントの通り動作していることが確認できます。
int n = 0; switch (1) { case 1: n += 1; // breakがないので n += 2 も実行されます。 case 2: n += 2; break; // breakがあるのでswitch文を抜けます。 case 9: n += 9; break; } printf("n = %d\n", n); // n = 3
Copyright © ITmedia, Inc. All Rights Reserved.