今までの学習で、変数や配列の格納場所を指すポインタの概念とその利用方法について学びました。更に、C言語では、関数を指すポインタの概念があります。
下記プログラムは、コマンドラインからの入力に応じて、2つの数値の足し算、引き算を行うプログラムです。このプログラムを通して、関数へのポインタの基本的な利用方法について学習します。
#include <stdio.h> /* 関数のプロトタイプ宣言 */ int add(int a, int b); int diff(int a, int b); int other(int a, int b); int main(int argc, char *argv[]){ int (*pfunc)(int a, int b); /* 1. */ int pa, pb; int i = 1; while(i < argc){ /* コマンドライン引数の最後まで繰り返す */ if(strcmp("add", argv[i]) == 0) /* 引数が"add"である場合 */ pfunc = add; /* 2. */ else if(strcmp("diff", argv[i]) == 0) /* 引数が"diff"である場合 */ pfunc = diff; /* 2. */ else /* 引数が"add", "diff"以外の場合 */ pfunc = other; /* 2. */ pa = atoi(argv[i+1]); /* 次のコマンドライン文字列を数値化 */ pb = atoi(argv[i+2]); /* 次のコマンドライン文字列を数値化 */ printf("%d\n", (*pfunc)(pa, pb)); /* 3. */ i += 3; /* 次のコマンドへ */ } } int add(int a, int b){ /* 2つの整数値を足して結果を返す関数 */ return(a + b); } int diff(int a, int b){/* 2つの整数値を引き算して結果を返す関数 */ return(a - b); } int other(int a, int b){/* 0を返す関数 */ return(0); }上記のプログラムを実行すると、下記のような結果が得られます (program はこのプログラムの実行形式ファイル名を示します)。どうしてこのような結果になるのかよく考えてみてください。
- int (*pfunc)(int a, int b); は2つの整数型引数を取り、整数型の返り値を 持つ関数へのポインタを入れる箱を宣言しています。 つまり、pfuncには関数へのポインタが入ることになります。
ここで、注意が必要なのは、int *pfunc(int a, int b); とは意味が異なる点です。 このように宣言すると、整数型データへのポインタを返す関数をプロトタイプ宣言 したことなってしまいます。- add()関数で、括弧を取り除いた形式 add がその関数を指し示すアドレスに なります。 従って、pfunc = add; は、add()関数へのポインタを pfunc に代入したことに なります。 プログラムでは、コマンドライン引数の文字列によって、使用する関数を替え、 その関数へのポインタを pfunc に代入していることになります。
- (*pfunc)(pa, pb) で実際に関数を呼び出しています。
$ program add 3 5 8 $ program diff 5 2 3 $ program add 4 5 diff 9 3 dummy 3 2 9 6 0しかし、このようなプログラムでは関数呼び出しを簡単な条件分岐で表現すればよいので、関数へのポインタを使うメリットはあまり実感できません。実際に関数ポインタが効力を発揮するのは、状態によって呼ぶ関数が決まっており、その状態を多次元配列テーブルで定義できる場合です。このような場合には、関数ポインタの入る多次元配列を用意しておけば、それによって呼ぶべき関数呼び出しを簡単に制御することができるため、複雑な作業を、かなりシンプルなプログラムで表現できます。
2003年2月25日 16:35 更新