C言語の研究2
第2章 プログラム制御文
プログラムは命令文であると言ったね。
一時、構造化プログラミングが叫ばれてから、順次、反復、選択の3つでプログラムを記述することが要求されるようになった。
順次とは上から下へ、反復とはクルクルと繰り返す、選択とはどれかを選ぶ、とでも言っておこうか。
順次なんかほっといても順次だ。
それでは一体、反復とか選択とはどのようにしてプログラミングすればよいのだろうか。
1 if文
もし〜ならばどうする、それとは違いもし〜ならばこうする。という条件を選択して分岐させるやり方である。
if(式1)
文1;
else if(式2)
文3;
else
文2;
たとえば、次のように書ける。
#include <stdio.h>
void main(int argc, char **argv)
{
int a = 0;
int b = 1;
if(a)
printf("aは真実です\n");
else if(b)
printf("bは真実です\n");
/*これが表示される */
else
printf("どっちも偽りです\n");
}
C言語の場合、1がONつまり真ということだったから、上の文ではbが真となる。だから、「bは真実です」が選択されて表示される。
2 switch文
if文もいいが、選択条件が多い場合、つまり多分岐の場合、switch文を使った方がスッキリする。
switch(式){
case 定数式1 :
文1;
case 定数式2 :
文2;
:
default :
文n;
}
こいつは、式の条件と合致したcaseの文を実行したら、ずっと順次に下のdefaultの文7まで実行してしまう。
これを避けて、if文のように合致した定数式の文だけを実行させて外に出させるためには、break; をいれてやるとよい。breakについては後で説明しよう。
次のプログラムを実行してみよう。
#include <stdio.h>
void main(int argc, char **argv)
{
int a = 100;
int b = 95;
int c = 89;
double ave;
char hyoka;
ave = (double)( (a + b + c) / 3);
switch((int)(ave / 10)){
case 10 :
hyoka = 'A';
break;
case 9 :
hyoka = 'B';
break;
case 8 :
hyoka = 'C';
break;
case 7 :
hyoka = 'D';
break;
default:
hyoka = 'E';
}
printf("平均点は%3.1fで、評価は%cです\n",ave,hyoka);
}
どうだろうか、「平均点は94.0で、評価はBです」と表示されただろうか。
3 while文
いまの2つが選択であったのに対して、反復いわゆるループを見てみよう。
次の場合はwhileの式が真でない場合は実行されない。
while(式)
文;
しかし、次の場合は、doに続く文はどんなことがあっても1回は実行される。
do
文; /*この文は最低1回は実行される*/
while(式);
whileにはこの2つの方法がある。
ちょっと次の例を見てみよう。nに1から1ずつ増加するiを足していって、nが10以上になる前にループから出て、nを表示するプログラムだ。nが10以上にならないでほしい場合だが、
<例1>
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
n=0;
i=1;
while(n < MAX)
n += i++;
printf("nの値は%dです\n",n);
}
この例1で、nの値は何になっているだろう。次の例2はどうだろう?
<例2>
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
n=0;
i=1;
do
n += i++;
while(n < MAX);
printf("nの値は%dです\n",n);
}
いずれも「nは10です」となる。おっかしいなぁ?
whileの条件を10より小さいとしているのに、なぜ10を越えたnの値が出力されてしまうのだろうか。
これを証明するために、例2の方の文の中にprintf文を加えて実行してみよう。
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
n=0;
i=1;
do{
printf("iは%d nは%d\n",i,n);
n += i++;
}
while(n < MAX);
printf("nの値は%dです\n",n);
}
これを実行してもらうと分かるのだが、i が1、2、3と順番になって、n がこれらを足し算して6になった時点で、while文はnが10より小さいと判定して、もう一度ループさせてしまう。
すると i が4になってnに加わって10になってしまうということだ。
これを避けるためにはどうしたらいいのだろうか。
ここでbreak様の再登場だ。breakには直近のループを抜けさせる役割がある。つまりこうだ。
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
n=0;
i=1;
do{
printf("iは%d nは%d\n",i,n);
n += i++;
if(n+i >= MAX) break; /*次のnの値を先に見て >=10なら抜ける */
}
while(n < MAX);
printf("nの値は%dです\n",n);
}
こうしておけば、n=6となってOKだぜ。
4 for文
ホウと感心してシャレている場合じゃない、先を急ごう。
for(式1; 式2; 式3)
文;
という形で、while文をよりコンパクトにできるすぐれものだ。
式1を最初に1度だけ読む。大体はここで初期化する式が入る。
式2は判定文が来る。これが真なら式3を実行する。
式3はカウントアップとか再初期化文が来る。
そして、式2、式3、式2、式3と繰り返す。
式2が偽りになったらループを脱出する。さっきの例でテストしてみよう。
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
for(n=0, i=1 ; n+i < MAX ; n += i++){
printf("i=%d
n=%d\n",i,n);
}
printf("nの値は%dです\n",n);
}
これもnが6となるようにした一例だが、式1にカンマで区切って複数の式を入れることが可能だ。つまり、2つ以上の変数を初期化できるということだ。
式2は判定文だ。n+i が10になる前にループから脱出さ。
式3はさっきから書いていたが、ちょっとややこしかったかも知れない。nに i を加えると同時に i を後置増分して1を加えている。n+=i, i++と2つ書くのを1つにしただけの話だ。例の桂小枝氏なら「簡潔にもホドがある」と言われるかも知れないねと言ったやつだ。
5 break と continue
breakについては、先ほど来言って来たように直近のループをぬけさせる文だ。
これとは別に、ループは抜けないものの、条件になったら処理をさせずにスキップさせるやつがある。
そいつがcontinue文だ。
今度はcontinue文でnが10にならないようにしてみようと思って、次のようにしたが、一体どうなるのだろうか。
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
for(n=0, i=1 ; n < MAX ; i++){
printf("iは%d nは%d\n",i,n);
if(n + i >= MAX)
continue;
else
n += i;
}
printf("nの値は%dです\n",n);
}
答えは無限地獄だ。なぜか分かるかね明智君。
つまり、私はn+i が10以上の場合はスキップさせておくと、for文の式2の判定でn が10以上になったら、勝手に終了するだろうと思った。とんでもないことであった。
スキップさせるのは、次にn+i が10を越えるとき、つまりnが6になった時からのcontinueより下の文だ。
だから、 n += i がスキップされて n は6になった後は一向に増加しない。
そしてi はfor文の式3によって、どんどんと1ずつ増加する。
だからnは6のままで、i の増加は永遠に止まらなくなっちゃった。
それじゃ次のようにしたらどうなの?
うまく行くが、あまりcontinueの真価が発揮されてないね、これは。
#include <stdio.h>
#define MAX 10
void main(int argc, char **argv)
{
int n, i;
for(n=0, i=1 ; n < MAX ; n += i++){
printf("iは%d nは%d\n",i,n);
if(n +i < MAX)
continue;
else
break;
}
printf("nの値は%dです\n",n);
}
それでは、continueの真価を発揮する場合を考えてみよう。
たとえば整数の2次元配列があって、これを全部加える計算をする場合に、マイナスの数値は加えないようなのが適しているのかな。次の例を見よう。
#include <stdio.h>
void main(int argc, char **argv)
{
int i ,j ;
int arr[3][3]={
{10, -1 ,10},
{10, -15, -99},
{10, 10, -80}
};
int plus = 0;
for(i = 0; i < 3 ; i++){
for(j = 0; j < 3 ;j++){
if(arr[i][j] < 0)
continue; /*0より小さい要素はスキップ */
plus += arr[i][j];
printf("arr[%d][%d] = %d\n",i, j, arr[i][j]);
}
}
printf("plusの値は%dです\n",plus);
}
どうだろうか?
0より小さい要素のprintf文は無視されて、0以上の要素がprintfされて、plusの値は50になった。
こういうのがcontinue文の用例だろうね。
ループ文のwhileやforは多用されるので十分な警戒が必要だ。
while(式)の式は必須だが、for(式1 ;式2 ; 式3 )の式1から3は省略可能だ。だから、
while(0); あるいは for( ; 0; ); とすれば、1回も実行されないし、
while(1以上の数値); あるいは for( ; ; ); は無限ループだ。