関数ポインタ

関数ポインタとは

ポインタとして保持できるのは、何も変数だけではありません。関数もまた、ポインタとして利用できます。これを、関数ポインタと言います。実際に、簡単なサンプルを見てみましょう。

listex3-fp-1:main.c
#include <stdio.h>

// 関数
void func1();
void func2();

void main() {
	void (*fp)() = func1;	//	関数ポインタをfunc1で初期化
	fp();	//	関数fpを実行
	fp = func2;
	fp();	//	関数fpを実行
}

//	関数1
void func1()
{
	printf("func1¥n");
}

//	関数2
void func2() 
{
	printf("func2¥n");
}
実行結果の例
func1
func2

このプログラムの8行目で定義されている、fpが、関数のポインタです。関数のポインタの書式は、以下の通りになっています。

期待される実行結果の例 関数ポインタの書式
型名 (*変数名)(引数)

という形になります。ここで用いている、func1()および、func2()は、戻り値の型がvoidであり、引数がないので、()無いが空白で、変数名がfpであることから、void (*fp)と宣言します。

型の定義の仕方こそ特殊ですが、関数ポインタも、ポインタ変数の一種です。ただ、そこに代入するのはあくまでも関数です。

引数のある場合のサンプル

続いて、関数に引数がある場合のケースを見てみましょう。以下のサンプルを実行してみてください。

listex3-fp-2:main.c
#include <stdio.h>

int max(int,int);
int min(int,int);

void main() {
	int (*cmp)(int,int) = max;	//	cmpの初期化と(max)
	int a = 1,b = 2;
	printf("%dと%dのうち、最大のものは、%d¥n",a,b,cmp(a,b));
	cmp = min;	//	cmpにminを代入
	printf("%dと%dのうち、最小のものは、%d¥n",a,b,cmp(a,b));
}

//	最大値を返す関数
int max(int m,int n)
{
	if(m > n){
		return m;
	}
	return n;
}

//	最大値を返す関数
int min(int m,int n)
{
	if(m < n){
		return m;
	}
	return n;
}
実行結果
1と2のうち、最大のものは、2
1と2のうち、最小のものは、1

関数の引数として関数ポインタを渡す

続いて、さらに高度な使い方を見てみましょう。関数ポインタは、さらに、他のポインタ同様、関数の引数として渡すことも可能です。ためしに、以下のサンプルを実行してみてください。

listex3-fp-3:main.c
#include <stdio.h>

//	関数ポインタを引数としてとる関数
void funcp(int (*)(int),int n);
//	関数ポインタに用いる関数
int dbl(int);
int hlf(int);


void main() {
	funcp(dbl,8);	//	引数として、関数を渡す
	funcp(hlf,8);	//	引数として、関数を渡す
}

void funcp(int (*f)(int),int n)
{
	printf("引数:%d 結果%d¥n",n,f(n));
}

//	最大値を返す関数
int dbl(int n)
{
	return n * 2;
}
int hlf(int n)
{
	return n / 2;
}
実行結果の例
引数 8 : 結果16
引数 8 : 結果4

4行目で、関数ポインタを引数として持つ関数、funcp()のプロトタイプ宣言をしています。プロトタイプ宣言の中で、関数ポインタを宣言するときは、以下のようになります。

引数内での関数ポインタの書式
型名 (*)(引数)

普通の引数と同様、関数ポインタの宣言から、変数名を抜いた形にすればよいことがわかります。関数名は、対応する関数の定義の中で記述します。15行目)

このサンプルでは、この引数として、関数dblおよび、hlfを与えています。引数として与えられた関数は17行目で実行されていますが。関数が違えば、当然のことながら実行結果は違ってきます。

このように、関数ポインタは、呼び出す関数を動的に変更する時に非常に便利です。