C言語:関数ポインタの型
三日坊主になるところだったので、久々に記事を書いてみる。
関数ポインタの型宣言のおさらい
C言語で関数ポインタを受け取る関数と言えば、qsort()が有名ですね。それではqsort()の宣言を見てみましょう。
void qsort(void *buffer, size_t count, size_t size, int (*comp)(const void *, const void *));
一番最後の引数compが、関数ポインタの宣言です。これは
int comp(const void *a, const void *b);
という型の関数ポインタを受け取ることを表します。
特徴的なのは、変数compの位置がおかしいことです。
関数ポインタを返す関数
変数名の場所がおかしいので、関数ポインタを返す関数は、非常に書きづらいものになります。でも書いてみましょう。
intを受け取り、char *を受け取り何も返さない(i.e. voidを返す)関数を返す関数はこうなります(←読みづらい・・・
void (*f1(int x))(char *);
お分かり頂けただろうか?
なぜこんな形になるのか
まず最初に、関数の宣言と、変数の宣言を比べてみましょう
戻り値の型 関数名(引数リスト); // 関数 変数の型 変数名; // 変数
ちょっと似てますね。変数名に引数リストを付けたものが、関数名に見えませんか?
ここで、戻り値の型を使って関数を宣言している。という風に考えてみましょう。
さて、関数ポインタの変数の型は
戻り値の型 (*変数名)(引数リスト);
となります。では、これらを組み合わせてみましょう。上の例で変数名としたところを、関数名に書き換えてみます。
戻り値の型 (*関数名(引数リスト))(引数リスト);
前のリストは、関数ポインタを返す関数の引数リスト、後のリストは、返された関数ポインタに与える引数リストです。
これをもうちょっと応用してみましょう。
関数ポインタの配列の宣言
配列の宣言は、
要素の型 配列名[要素数];
でしたよね。それでは、関数ポインタを要素とする配列 を宣言してみましょう。
さっきと同じ考えで、変数名の部分を配列っぽく書きます。
戻り値の型 (*配列名[要素数])(引数リスト);
これは結構効きます。例えば以下のようなスイッチ文を
switch (i) { case 0: printf("%lf\n", sin(x)); break; case 1: printf("%lf\n", cos(x)); break; case 2: printf("%lf\n", tan(x)); break; }
関数ポインタの配列を用意すれば、
double (*funcs[])(double) = { sin, cos, tan }; printf("%lf\n", funcs[i](x));
と書き換えることができます。結構有名かも?
※結構適当に書いています。間違いがあったらご一報下さい