C 言語の関数ポインタ
この記事では、C 言語で関数ポインタを使用する方法を紹介します。
C 言語で関数ポインタを定義するには void (*func)()
記法を使用する
関数ポインタは、動的な関数呼び出しや、オブジェクト指向デザインや型ジェネリックプログラミングに似た独自のメソッドを含む構造体などの高度な機能を実装する C 言語プログラミングのもう一つの構成要素です。関数ポインタの宣言は複雑な構文を持つことがあることに注意してください。以下の例では、printInt
関数のアドレスを代入しているが、printInt
関数は単一の int
引数を取るが、void (*func)(void)
型の関数ポインタには func
という名前の関数ポインタが代入されています。一度 func
という名前の関数ポインタを定義すれば、通常の関数呼び出し表記法 func(arg)
または間接参照演算子 (*func)(arg)
を用いて呼び出すことができます。
#include <stdio.h>
#include <stdlib.h>
void printInt(int x) { printf("printed from printInt: %d\n", x); }
int main() {
int input1 = 10233;
void (*func)(int) = printInt;
func(input1);
(*func)(input1);
exit(EXIT_SUCCESS);
}
出力:
printed from printInt: 10233
printed from printDouble: 11.234000
あるいは、typedef
を用いて関数ポインタの新しい型のエイリアスを定義し、コードをより読みやすくすることもできます。関数の型が異なる場合は、typedef
文を別々に定義する必要があることに注意してください。しかし、printInt
と printDouble
の両方の関数のアドレスは FuncPtr
型の変数に格納されています。特定の関数のアドレスは明示的な &
演算子を用いて取得することも、次の例で示されているように関数名そのものを暗黙的に代入することもできます。
#include <stdio.h>
#include <stdlib.h>
typedef void (*FuncPtr)();
void printInt(int x) { printf("printed from printInt: %d\n", x); }
void printDouble(double x) { printf("printed from printDouble: %f\n", x); }
int main() {
int input1 = 10233;
double input2 = 11.234;
FuncPtr func1 = printInt;
FuncPtr func2 = printDouble;
func1(input1);
func2(input2);
exit(EXIT_SUCCESS);
}
出力:
printed from printInt: 10233
printed from printDouble: 11.234000
関数ポインタ配列を用いた型汎用プログラミング機能の実装
他のオブジェクトと同様に、括弧 []
記法で関数ポインタの配列を定義することができます。この配列を利用して、ランタイム中に特定の関数を簡単に選択して呼び出すことができます。キーワード _Generic
を使用していることに注意してください。これは switch
のような式で、制御する式の型の評価に基づいて特定のケースを選択することができます。したがって、以下のコード例では、switch
条件で渡された変数の型に応じて、対応する print
関数を呼び出すようにしています。なお、enum
型は異なるケースの定数値を定義するためにも利用されます。
#include <stdio.h>
#include <stdlib.h>
enum TYPE { INTEGER, DOUBLE, INVALID };
#define typename(x) \
_Generic((x), int: INTEGER, double: DOUBLE, default: INVALID)
typedef void (*FuncPtr)();
void printInt(int x) { printf("printed from printInt: %d\n", x); }
void printDouble(double x) { printf("printed from printDouble: %f\n", x); }
int main() {
int input1 = 10233;
double input2 = 11.234;
FuncPtr func_ptrs[] = {printInt, printDouble};
switch (typename(input1)) {
case INTEGER:
func_ptrs[INTEGER](input1);
break;
case DOUBLE:
func_ptrs[DOUBLE](input1);
break;
case INVALID:
printf("No corresponding type found!\n");
default:
break;
}
switch (typename(input2)) {
case INTEGER:
func_ptrs[INTEGER](input2);
break;
case DOUBLE:
func_ptrs[DOUBLE](input2);
break;
case INVALID:
printf("No corresponding type found!\n");
default:
break;
}
exit(EXIT_SUCCESS);
}
出力:
printed from printInt: 10233
printed from printDouble: 11.234000