C言語 | 高度な機能 | 関数ポインター
関数にもアドレスがある。関数のアドレスをポインターへ保存すると、実行時に呼び出す関数を選択できる。
関数アドレスの保存
関数ポインターは、戻り値型、ポインター名、引数型で宣言する。
戻り値型 (*識別子)(引数型リスト)
たとえば、int (*pf)(const char *) は、文字列を受け取り int を返す関数へのポインターである。括弧は重要であり、int *pf(const char *) と書くと int * を返す関数の宣言になる。
次の関数があるとする。
int Function(int , void *);
アドレスは次のように保存する。
int (*pf)(int , void *) = Function;
ポインター経由の呼び出しには、どちらの構文も使える。
i = pf(iValue , pointer);
i = (*pf)(iValue , pointer);
コード1
#include <stdio.h>
void f(void) {
printf("Kitty on your lap\n");
}
int main() {
void (*fp)(void) = f;
fp();
(*fp)();
return 0;
}
関数ポインターはコールバックに役立つ。イベントが発生したときに呼び出す関数をシステムへ登録できる。また、複数のハンドラーを配列へ保存すると、拡張可能な設計を作れる。
コールバック関数の登録
次の例では、レジストリーにコールバックを追加、削除する。
コード2
#include <stdio.h>
#define ADD_FUNC 2
#define REMOVE_FUNC 4
#define RESULT_OK 0
#define RESULT_ERROR 1
typedef void(*REGISTERPROC)(const char * , int);
int RegisterFunc(REGISTERPROC func , int mode) {
static int iLen = 0;
static REGISTERPROC procs[256];
int iCount;
char removed;
switch(mode) {
case ADD_FUNC:
if (iLen == 256) return RESULT_ERROR;
for(iCount = 0 ; iCount < iLen ; iCount++)
if (procs[iCount] == func) return RESULT_ERROR;
procs[iLen++] = func;
for(iCount = 0 ; iCount < iLen ; iCount++)
procs[iCount]("Add" , iCount);
break;
case REMOVE_FUNC:
removed = 0;
for(iCount = 0 ; iCount < iLen ; iCount++) {
if (procs[iCount] == func) {
for(; iCount < iLen - 1 ; iCount++)
procs[iCount] = procs[iCount + 1];
iLen--;
for(iCount = 0 ; iCount < iLen ; iCount++)
procs[iCount]("Remove" , iCount);
removed = 1;
break;
}
}
if (!removed) return RESULT_ERROR;
break;
default:
return RESULT_ERROR;
}
return RESULT_OK;
}
void Function1(const char *msg , int value) { printf("Function1 : %s , %d\n" , msg , value); }
void Function2(const char *msg , int value) { printf("Function2 : %s , %d\n" , msg , value); }
void Function3(const char *msg , int value) { printf("Function3 : %s , %d\n" , msg , value); }
int main() {
RegisterFunc(Function1 , ADD_FUNC);
RegisterFunc(Function2 , ADD_FUNC);
RegisterFunc(Function3 , ADD_FUNC);
RegisterFunc(Function2 , REMOVE_FUNC);
RegisterFunc(Function3 , REMOVE_FUNC);
return 0;
}
REGISTERPROC はコールバックポインター型の別名である。レジストリーは最大256個のコールバックを受け取り、重複を防ぎ、項目が追加または削除されるたびに残っているコールバックへ通知する。