(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) /*実行する際の指定*/
} |
|