Objective-C でもブロックの構文をすぐに忘れてしまうように、C言語の関数へのポインタもすぐに忘れてしまいます。備忘録として残しておきます。
そもそも関数へのポインタがどのように活用されるのかというと、GUIにおいてユーザーとのインターアクションで生じるイベントをハンドルする場合や、ソート関数にユーザーがソート方式を指定することができる柔軟性を与えるといった場合があります。具体的には、iOS 等のアプリケーションのボタンを想像してもらうと分かりやすいと思いますが、ユーザーがそのボタンを押した時にある処理をしたい場合、このボタンを作成時に関数を指定してくと便利そうです。また、ソート関数においても昇順か降順だけでなく、ユーザーにある程度の自由を与えたほうが拡張性の高いソート関数になります。C の qsort もまさにこのデザインです。この2つの例のように、関数を指定したい時に関数へのポインタが必要になります。qsort の宣言はこのようになっています。
1 2 | void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); |
この int(*cmpar)(const void *, const void *) の部分ですね。もう個人的に何を言っているのか分かりません。まずは、関数ポインタの基本形から。
1 | void (*foo)(int); |
この例は、foo というポインタがあります。これは関数へのポインタで、この関数は int を受け取り、void を返します。宣言は左から順に読みながら、一番内側のカッコを探します。この基本形を頭に入れておいて、
1 | void *(*foo)(int *); |
この例です。アスタリスク(*) がいくつもあって本当に分かりにくい。これは、foo というポインタがまずあります。この関数は int へのポインタを受け取り、void へのポインタを返します。void ポインタは何でも指すことができるポインタです。qsort の引数も void のポインタです。int でも double でも問題なく引数に取ることができます。その代償に、ソートする要素の一つあたりのサイズとソートするデータ全体のデータの総数を渡す必要があります。
最後に関数ポインタをどのように使うか簡単な例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <stdio.h> void my_int_func(int x){ printf( "%d\n", x ); } int main(){ void (*foo)(int); foo = &my_int_func; /* call my_int_func (note that you do not need to write (*foo)(2) ) */ foo( 2 ); /* but if you want to, you may */ (*foo)( 2 ); return 0; } |
ポイントされる関数の記述は通常通りです。関数へのポインタの宣言方法は上記で見たとおりですね。関数へのアドレスを宣言したポインタに代入し、そのアドレスを格納している変数をそのまま関数呼び出しに使用しています。