7.8 ループのテクニック

○ while(1)とbreak文

 while()文とbreak文をくみあわせることにより様々な処理の制御ができるようになります。break文は、while , do , for , または  switch文で囲まれた繰り返しブロックから抜けるために使われます。例えばリストtest7_8.cを基に、'.'を入力すると終了するようにするには次の様になります。

 

リストtest7_16.c

// stdin から '.' が入力されたならば

// 無限ループをぬけます。    

// もし、そうでなければ stdout に

// エコーします。         

#include <stdio.h>         

main()         

{         

    int c;         

 

    while(1) {

      c = getchar();     

      if (c == '.' )      

        break;     

      printf("%c",c);    

    }         

}         

 

 リストtest7_16.cの場合では、あえてwhile-breakのかたちにしなくてもかまいませんが、入力関数が入力した値を関数の値としてとらないような場合やループの終了条件が複数箇所ある場合などはwhile-breakで組み立てる方がわかり易いプログラムになります。

 その様な例として総和を求めるプログラムを示します。このプログラムの場合、0を入力したら終了、総和が100以上になったら終了という2つの終了条件を付けます。

 

リスト test7_17.c

#include<stdio.h>

main()

{

     int    i,s;

s = 0; // 総和を0にする

     while(1){

printf("?"); // 入力の促進

         scanf("%d",&i);

         if( i == 0){ break; } // 入力が0なら終了

s = s + i; // 総和の計算

         printf("in = %d sum = %d\n",i,s);

if( s >= 100){ break; } // 総和が100以上なら終了

}

}

 

○抱え飛び込みはやめよう

 リストtest7_8.c、test7_16.cと同様なプログラムはリストtest7_18.cのように変数cに初期値を持たせてwhileのループに飛び込もむこともできますが、あまりきれいな方法ではありません。

 

            リストtest7_18.c

#include<stdio.h>

main()

{

    int c;

    c = 1; // 0以外なら何でもいい

    while( c ){

        c = getch();

        printf("%c" , c);

    }

}

○0から数えるか、1から数えるか

 for()等を使ったプログラムで、例えば1から10までを表示するプログラムの場合、リストtest7_19.cc、リストtest7_20.cのようにループ・カウンターを0から始めても、1から始めても、同様の結果が得られます。

 

リストtest7_19.c <1から数える>
#include <stdio.h>
main()
{
    int   i;
    for(i=1;i<=10;i++)
        printf("i= %d\n",i);
}

 
   リストtest7_20.c <0から数える>
   #include <stdio.h>
   main()
    {
       int   i;
        for(i=0;i<10;i++)
           printf("i= %d\n",i+1);
    }
 

 

たとえば配列をとってそれを操作するような場合、配列は0番目からとられますので、ループカウンターも0から始めるほうが適当だと言えます。その例をリストtest7_21.cに示します。

 実は、C言語だけでなく、日常生活でも0から番号をふるほうが、桁上がりのことを考えると結構便利な場合がよくあります。たとえば、20個、何か物があって順に番号をふるとします。そのとき0から番号をふっていくと、はじめの10個は10の桁がなしで、あとの半分は10の桁はすべて1となり、整理がしやすくなります。

 

リストtest7_21.c

#include <stdio.h>      

main()      

{      

    int i; 

    int x[10];

    for (i = 0; i < 10; i++) {      

        x[i] = i * i;      

        printf("x[%d] = %d \n", i, x[i]);      

    }      

}      

 

○フラグを使う

 どんな言語でも言語自身のもつ制御構造だけでは簡単にアルゴリズムが記述できない場合がよくあります。そんな場合にはフラグを使います。同じ意味でスイッチという言葉が使われる場合もあります。

 素数を求めるプログラムを考えます。素数は1と自分自身でしか割り切れない数ですから、数をnとすると、2からn−1までの数で割り算をし、1度でも割り切れると、素数に該当しなくなります。

 この2からn−1までのループの中で、あまり(モジュロ、剰余)を計算し、あまりが0ならループを脱出するようにします。最後までループが回って終了すればその数は素数ということになります。このような場合、ループを途中終了したのか、そうでないのかは、制御構造だけでは判りません。

 そこでフラグを準備してループに入り、途中終了の場合はそのフラグを操作して抜け出すようにします。そうすると、あとでフラグをみてループの終了状況の違いが判断できます。

 

<リスト23>

/*   sosu.c     */

main()

{

    int   i,j,sosu;

 

    for(i=2;i<=100;i++){

         sosu = 1;

        for(j=2;j<i;j++){

             if((i % j )== 0){

                sosu = 0;

                break;

            }

        }

        if (sosu)

            printf(" %2d",i);

    }

}

 

 

<図13>リスト23の実行結果

 2  3  5  7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

 

ループや関数からの抜け出し

 C言語ではループや関数を抜け出す方法には、文のbreak、return、それと関数のexitの3種類があります。breakはそのすぐ外のループに抜け出します。returnは今いる関数から抜け出します。exitはプログラムからOSに抜け出します。

 たとえば2重ループの外に抜けたい場合、これらの文単独では実現できません。

ループの外にラベルを置いてgoto文で飛ばす手もありますが、これは禁じ手だと思います。リスト24のようにフラグを立ててブレイクするか、リスト25のようにループ変数をリセットするのが正当でしょう。

<リスト24>

/*   break01     */

main()

{

    int i,j,k,br_flag=0;

    for(i=0;i<10;i++){

        for(j=0;j<10;j++){

            scanf("%d",&k);

            if(k == 0)

                br_flag = 1;

            if(br_flag == 1)

                break;

        }

        if(br_flag == 1)

            break;

    }

}

 

<リスト25>

/*   break02.c    */

main()

{

    int i,j,k;

    for(i=0;i<10;i++){

        for(j=0;j<10;j++){

            scanf("%d",&k);

            if(k == 0){

                i= 10;j = 10;

            }

        }

    }

}

 

 C言語での制御としてfor文、while文、if文、break、以外にdo-while文、continue文、goto文、switch文があります。ですが、初めのうちは、これらはあまり使用しないほうがいいと思いますし使用しなくともまったく不自由はありません。