◆◆◆◆アマゾンで購入する◆◆◆

13.プログラムの場所をポインタで 
           内    容
(1)プログラムのアドレス
(2)関数ポインタ設定
(3)ポインタによる関数呼出し
(4)状態遷移図の制御
(5)注釈削除プログラム
(6)関数ポインタの配列


(1)プログラムのアドレス

コンピュータでは,以下の処理が最初に行われます。

  ■プログラムをデータとして読み込み。
  ■その先頭に制御を移す。

これを IPL(Initial Program Loader)といい,
これがOSの第一歩です。

すなわち,システム記述用言語では,
最低限,以下のことができなければなりません。

 ■プログラムが入っているアドレスを扱う。
 ■特定のアドレスに制御を渡す。

プログラムのアドレスも,
Cではポインタとして扱います。

(2)関数ポインタ設定

これを宣言するには,
関数のプロトタイプ宣言とデータのポインタ宣言の
あいの子みたいな宣言を行います。

   void (*key_mode)(char ch);

[意味]
 @ key_mode という変数に関数へのポインタが入る。
 A 関数の引数は char 型。

関数へのポインタを設定するには,
以下のように関数名を指定します。

void (*key_mode)(char ch);
void k_init(char ch);
  ・
  ・
  ・
main()
{
  key_mode = k_init; /* ()を付けないことに注意 */
}

(3)ポインタによる関数呼出し

ポインタで指定されている関数を
呼び出すには,次のように記述します。

 key_mode = k_init;
   ・
   ・
   ・
(*key_mode)(ch); /* この表現はk_init(ch);と同等 */

一方,引数が同じで,異なる処理を行うには,
switch文では,たとえば以下のように記述します。

 int CD;
 CD=2;
 ……
 switch(CD)
 {
   case 0: fn00(ch);break;
   case 1: fn01(ch);break;
   case 2: fn02(ch);break;
   case 3: fn03(ch);break;
 }


関数へのポインタを使うことによって,
以下のようなすっきりした表現が可能です。

  void (*fnX)(char ch);
  fnX= fn02;
 ……
  (*fnX)(ch);

(4)状態遷移図の制御

“/*”と“*/”で囲まれる部分を取り除く処理,
すなわち,ソースプログラムから注釈を
取り除く処理を考えてみましょう。

なお,簡単化のため,
文字列範囲を無視する処理は
考えないことにします。

このような処理は,一般に,
状態遷移図で表現することができます。



このように状態遷移図で表現されるような問題に
関数ポインタを適用すると,

関数を,それぞれのモードに対応付けることで,
状態遷移図と関数処理の対応がつきやすい
プログラムを作ることができます。

@最初のモード

 [関数]
   void k_init(char CH)
   { if(CH=='/') key_mode=k_com_wait;
     else StringSet(CH);
   }

A'*'を待つモード



 [関数]
   void k_com_wait(char CH)
   {  if (CH=='*') key_mode=k_com_in;
     else if(CH=='/') StringSet(CH);
     else
     {
       StringSet('/');StringSet(CH);
       key_mode=k_init;
     }
    }

Bコメント中



 [関数]
   void k_com_in(char CH)
   {  if (CH=='*') key_mode=k_com_out;
    }

C'/'を待つモード


 [関数]
   void k_com_out(char CH)
   {  if (CH=='/') {StringSet(' ');key_mode=k_init;}
     else if(CH!='*') key_mode=k_com_in;
   }

(5)注釈削除プログラム

状態遷移図による注釈削除のプログラムの
全体を示します。

このプログラムを参考にして,
文字列内はコメントではないと
判別する状態遷移図を描いて,
拡張してみるとよいかもしれません。

[プログラムの全体]
#include "stdafx.h"
#define DTMAX 1000

void (*key_mode)(char ch);
void k_init(char ch);
void k_com_wait(char ch);
void k_com_in(char ch);
void k_com_out(char ch);

static char OutDT[DTMAX+1]="";
static int ptrDT,ipt;

void StringSet(char CH)
{
 if(CH == '\n' )
 {
   OutDT[ptrDT++]='\n';
   ipt=0;
 }
  else if((CH<0) | (CH>=20) )
 {
   OutDT[ptrDT++]=CH;

    /* 以下,表示を見やすくするために */
   /* 80文字ずつ区切る処理。     */
   /* 文の解釈のときは不要       */

   if((++ipt)>=80)
   { OutDT[ptrDT++]='\n';
     ipt=0;
   }
  }
}

int main(int argc, char* argv[])
{
  char CH;
  ptrDT=0;
  ipt=0;
  key_mode=k_init; /* 初期モード*/
  CH=getchar(); /* 1文字入力*/
  while(CH != EOF) /* EOFZでない間*/
  {
    (*key_mode)(CH);/* 現モード処理*/
    CH=getchar(); /* 1文字入力 */
  }
  printf("\n-----注釈除去--\n");
  OutDT[ptrDT++]=0x00;printf("%s",OutDT);
}
/* 以下状態遷移 */
void k_init(char CH)
{
  if(CH=='/') key_mode=k_com_wait;
  else StringSet(CH);
}
void k_com_wait(char CH)
{
  if (CH=='*') key_mode=k_com_in;
  else if(CH=='/') StringSet(CH);
  else
  {
    StringSet('/');
    StringSet(CH);
    key_mode=k_init;
  }
}

void k_com_in(char CH)
{
 if (CH=='*') key_mode=k_com_out;
}

void k_com_out(char CH)
{
  if (CH=='/')
  {
     StringSet(' ');
     key_mode=k_init;
  }
  else if(CH!='*') key_mode=k_com_in;
}


(6)関数ポインタの配列

関数ポインタも配列として扱うことができます。
この方法は,
インタプリタやエミュレータのコマンド/命令処理等に
多く使われます。

/* 関数ポインタ配列の宣言 */
static void (*key_Add[4])(char ch);
   ・
   ・
   ・
int _tmain()
{
  key_Add[0]=k_init; /* 関数ポインタ値の設定*/
  key_Add[1]=k_com_wait;
  key_Add[2]=k_com_in;
  key_Add[3]=k_com_out;
   ・
   ・
   ・

  (*key_mode[j])(ch) /*実行する際の指定*/
}


 1.C言語の誕生

 2.C言語の仲間たち

 3.新しい言語をすばやく覚えるには

 4.Cでプログラミング

 5.データの入れ物 変数の考え方

 6.注釈・定数

 7.プログラムの実行順序

 8.配列

 9.C言語特有の代入文・制御構造

10.関数の話

11.構造体と共用体

12.ビット演算

13.プログラムの場所をポインタで

14.ファイルの入出力

15.色々な便利な方法