関数ポインタ


関数のエントリポイント

コンパイラは、コンパイル時に各関数のエントリポイントを作成します
プログラムの実効時に関数が呼び出されると、実行制御はこのエントリポイントに移行します

エントリポイントは、言わば関数のアドレスです
アドレスであるということはポインタ変数をもつことができます

関数ポインタは、アドレスを格納する関数と同じ戻り値の型のポインタ変数を用意します
仮引数がある場合は、続けて仮引数も指定します
宣言時には、優先順位の関係から必ずポインタ名に ( ) をつけます

型 (*変数名) (仮引数);

関数のエントリポイントをポインタに格納すれば、このポインタから関数を呼び出せます
こうすることで、間接参照で関数を呼び出しすることができるのです
#include <stdio.h>

void func(void);

int main() {
        void (*po)() = func;
        po();
        return 0;
}

void func() {
        printf("Kitty on your lap");
}
void (*po)() = func に注目してください
これは、void型のポインタ変数にfunc関数のエントリポイント(アドレス)を渡しています
このように、関数のアドレスをポインタ変数に格納することができます

呼び出しは、通常の関数同様に po() とやることで呼び出していますが
これでは通常の関数なのかポインタなのかわかりにくいプログラムになってしまいます
そこで、多くのプログラマは次のようにこれがポインタからの間接参照であることを表す記述法を用います

(*po)();

こうすれば、一見しただけでポインタからの間接参照であることがわかりますね
次のプログラムは、さらに仮引数と戻り値を指定した関数の間接参照の例です
#include <stdio.h>

int func(int , int);

int main() {
        int (*po)(int , int) , i;
        po = func;

        i = (*po)(10 , 3);
        printf("%d" , i);
        return 0;
}

int func(int i , int j) {
        return i << j;
}
関数のポインタで注目するべきは、間接参照であることです
ポインタに関数のアドレスを代入できるというのは、非常に斬新なことです
これまで、関数を呼び出すには直接関数名を指定する以外にありませんでした
しかし、間接参照することで関数の配列を実現することができます

関数の配列がどれだけ斬新なものかは想像がつくでしょう
この機能によって、プログラムの実行時に直接実行する関数を指定することができます
しかも、非常に簡易にです
#include <stdio.h>

void kitty(void);
void sakura(void);
void hina(void);

int main() {
	void (*po[])() = { kitty , sakura , hina };
	int i;

	printf("実行する関数の番号を指定してください 0〜2>");
	scanf("%d" , &i);
	if ((i < 0) | (i > 2)) return 0;
	(*po[i])();
	return 0;
}

void kitty() {
	printf("Kitty on your lap\n");
}

void sakura() {
	printf("Card Captor SAKURA");
}

void hina() {
	printf("LOVE HINA");
}
独自の開発環境や、マクロ用のインタプリンタの開発など
低レベルなシステムプログラムでは、この機能は活用されます
当然、アプリケーションでもアイデアはしだいでは非常に強力な機能になるでしょう



前のページへ戻る次のページへ