Page 24 補足:ループの使い方

例えば次のような流れのコードがあったとします。

/* 関数(func_*の中身は省略) */

int main(){
   int result;

   result = func_1();
   if(result == -1){
      return 1;
   }

   result = func_2();
   if(result == -1){
      return 1;
   }

   result = func_3();
   if(result == -1){
      return 1;
   }

   result = func_4();
   if(result == -1){
      return 1;
   }

   result = func_5();
   if(result == -1){
      return 1;
   }

   return 0;
}

func_1〜func_5の関数をそのまま実行し、チェックをするだけの処理ですが、チェック処理として同じ内容を何度も繰り替えしています。(↓強調表示した部分です)

/* 関数(func_*の中身は省略) */

int main(){
   int result;

   result = func_1();
   if(result == -1){
      return 1;
   }

   result = func_2();
   if(result == -1){
      return 1;
   }

   result = func_3();
   if(result == -1){
      return 1;
   }

   result = func_4();
   if(result == -1){
      return 1;
   }

   result = func_5();
   if(result == -1){
      return 1;
   }

   return 0;
}

同じのを何度も書くのは面倒ですし、冗長で見た目も汚いし、修正も手間がかかります。

こんな時、ループを使うと処理の記述の重複を避けることができます。

/* 関数(func_*の中身は省略) */

int main(){
   int result;
   int loop;

   for(loop = 0; loop < 5; loop ++){
      switch(loop){
      case 0:
         result = func_1();
         break;
      case 1:
         result = func_2();
         break;
      case 2:
         result = func_3();
         break;
      case 3:
         result = func_4();
         break;
      case 4:
         result = func_5();
         break;
      }
      if(result == -1){
         return 1;
      }
   }

   return 0;
}

このようにしておくと、後で追加するときや修正する時に楽になります。

もっと簡単に構造のみを書くと、次のようになります。

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

      /* 共通処理 */

      switch(i){
      case 0:
         /* 処理A */
         break;
      case 1:
         /* 処理B */
         break;

      /* 省略 */

      }

      /* 共通処理 */

   }

共通処理の部分に全処理で共通して行うこと、一般的には例外処理・エラー処理などを入れておけば、それぞれの処理A、処理B・・・では処理の記述が不要になります。

for文でセットする数字やcaseで分岐するときの数字はダイレクトに書いてもいいのですが、列挙体を使うことをおすすめします。理由は追加時、挿入時の変更が容易であるからです。(↓「PROC」はprocedureの略)

enum {
   PROC_START = 0,/* ダミー */
   PROC_A,        /* 処理A */
   PROC_B,        /* 処理B */
   PROC_C,        /* 処理C */
   PROC_D,        /* 処理D */
   PROC_END       /* ダミー */
};

/* ---------------- */

   for(i = PROC_START; i < PROC_END; i++){

      /* 共通処理 */

      switch(i){
      case PROC_A:
         /* 処理A */
         break;
      case PROC_B:
         /* 処理B */
         break;

      /* 省略 */

      }

      /* 共通処理 */

   }

というプログラムで、PROC_Gを追加したい時は

enum {
   PROC_START = 0,/* ダミー */
   PROC_A,        /* 処理A */
   PROC_B,        /* 処理B */
   PROC_C,        /* 処理C */
   PROC_D,        /* 処理D */
   PROC_G,        /* 処理G */
   PROC_END       /* ダミー */
};

/* ---------------- */

   for(i = PROC_START; i < PROC_END; i++){

      /* 共通処理 */

      switch(i){
      case PROC_A:
         /* 処理A */
         break;
      case PROC_B:
         /* 処理B */
         break;

      /* 省略 */

      case PROC_G:
         /* 処理G */
         break;

      }

      /* 共通処理 */

   }

と、少し追加するだけで済みますし、処理AとBの間にGを入れたい場合は、

enum {
   PROC_START = 0,/* ダミー */
   PROC_A,        /* 処理A */
   PROC_G,        /* 処理G */
   PROC_B,        /* 処理B */
   PROC_C,        /* 処理C */
   PROC_D,        /* 処理D */
   PROC_END       /* ダミー */
};

/* ---------------- */

   for(i = PROC_START; i < PROC_END; i++){

      /* 共通処理 */

      switch(i){
      case PROC_A:
         /* 処理A */
         break;
      case PROC_G:
         /* 処理G */
         break;
      case PROC_B:
         /* 処理B */
         break;

      /* 省略 */

      }

      /* 共通処理 */

   }

で、OKです。switch内の処理Gの位置はどこでもいいのですが、せっかく処理順に並んでいるので、AとBの間に入れた方がいいでしょう。ダミーはループ変数のセット、継続条件の指定に使用するために作ったものであり、実際の処理順を入れ替えても、ループの部分は変更することなくプログラムの修正ができます。

PROC_STARTが一回分余分にループを回ることになります。それが困る場合は、PROC_STARTを定義にしておく

#define PROC_START (0)

enum {
   PROC_A = PTOC_START /* 処理A */
   PROC_B,        /* 処理B */
   PROC_C,        /* 処理C */
   PROC_D,        /* 処理D */
   PROC_END       /* ダミー */
};

か、continueで処理を飛ばしてしまえばOKです。

   for(i = PROC_START; i < PROC_END; i++){

      /* 共通処理 */

      switch(i){
      case PROC_START:
         continue;
         break;
      case PROC_A:
         /* 処理A */
         break;
      case PROC_B:
         /* 処理B */
         break;

      /* 省略 */

      }

      /* 共通処理 */

   }

なお、関数ポインタが使えるのであれば、関数ポインタを使うともっとシンプルになります。

/* 関数(func_*の中身は省略) */

int main(){
   int result;
   int loop;
   int (*func[])() = {func1, func2, func3, func4, func5};

   for(loop = 0; loop < 5; loop ++){
      result = func[loop]();
      if(result == -1){
         return 1;
      }
   }

   return 0;
}

順番が一定でない場合はwhileやループ変数の足し算のないforを使用します。その場合は毎回処理番号をセットする必要があります。

   for(endflag = 0; endflag != 1;){

      /* 共通処理 */

      switch(i){
      case PROC_A:
         func_a();   /* 処理A */
         i = PROC_B;
         break;
      case PROC_B:
         result = func_b();   /* 処理B */
         if(result == 1){  /* 処理結果によって分岐 */
            i = PROC_C;
         }
         else {
            i = PROC_D;
         }
         break;

      /* 省略 */

      case PROC_G:
         result = func_g();   /* 処理G */
         if(result == 1){  /* 処理結果によって分岐 */
            endflag = 1;   /* 終了 */
         }
         else {
            i = PROC_A;
         }
         break;

      }

      /* 共通処理 */

   }

戻る