C Language
関数ポインタ
サーチ…
前書き
関数ポインタは、データ型の代わりに関数を指すポインタです。これらは、実行時に呼び出される関数の可変性を可能にするために使用できます。
構文
returnType(* name)(parameters)
typedef returnType(* name)(parameters)
typedef returnTypeの名前(パラメータ)。
名前*名前;typedef returnTypeの名前(パラメータ)。
typedef名前* NamePtr;
関数ポインタの割り当て
#include <stdio.h>
/* increment: take number, increment it by one, and return it */
int increment(int i)
{
printf("increment %d by 1\n", i);
return i + 1;
}
/* decrement: take number, decrement it by one, and return it */
int decrement(int i)
{
printf("decrement %d by 1\n", i);
return i - 1;
}
int main(void)
{
int num = 0; /* declare number to increment */
int (*fp)(int); /* declare a function pointer */
fp = &increment; /* set function pointer to increment function */
num = (*fp)(num); /* increment num */
num = (*fp)(num); /* increment num a second time */
fp = &decrement; /* set function pointer to decrement function */
num = (*fp)(num); /* decrement num */
printf("num is now: %d\n", num);
return 0;
}
関数から関数ポインタを返す
#include <stdio.h>
enum Op
{
ADD = '+',
SUB = '-',
};
/* add: add a and b, return result */
int add(int a, int b)
{
return a + b;
}
/* sub: subtract b from a, return result */
int sub(int a, int b)
{
return a - b;
}
/* getmath: return the appropriate math function */
int (*getmath(enum Op op))(int,int)
{
switch (op)
{
case ADD:
return &add;
case SUB:
return ⊂
default:
return NULL;
}
}
int main(void)
{
int a, b, c;
int (*fp)(int,int);
fp = getmath(ADD);
a = 1, b = 2;
c = (*fp)(a, b);
printf("%d + %d = %d\n", a, b, c);
return 0;
}
ベストプラクティス
typedefの使用
関数ポインタを毎回宣言するのではなく、 typedef
を使うほうが便利かもしれません。
関数ポインタのtypedef
を宣言する構文は次のとおりです。
typedef returnType (*name)(parameters);
例:
次のような関数compare
への関数ポインタを期待する関数sort
()があることを確認します。
compare - ソート関数に供給される2つの要素の比較関数。
"compare"は、2つの要素が等しいとみなされると0を返すことが期待され、最初の要素がある意味では "大きい"場合は正の値を返し、そうでない場合は関数が負の値を返します。後者よりも「小さい」)。
typedef
なければ、次のように関数ポインタを関数の引数として渡します。
void sort(int (*compare)(const void *elem1, const void *elem2)) {
/* inside of this block, the function is named "compare" */
}
typedef
、次のように記述します。
typedef int (*compare_func)(const void *, const void *);
sort
の関数シグネチャを次のsort
に変更することができます。
void sort(compare_func func) {
/* In this block the function is named "func" */
}
どちらのsort
定義も、フォームの任意の関数を受け入れます
int compare(const void *arg1, const void *arg2) {
/* Note that the variable names do not have to be "elem1" and "elem2" */
}
関数ポインタは、型のポインタプロパティを含めるべき唯一の場所です。例えば、 typedef struct something_struct *something_type
ような型を定義しようとしないでください。これは、stdio.h FILE
型(これはポインタではないことに気づくでしょう)のような、API呼び出し側が直接アクセスするはずのメンバーを持つ構造体にも当てはまります。
コンテキストポインタの取得。
関数ポインタは、ほとんどの場合、ユーザが指定したvoid *をコンテキストポインタとして取ります。
例
/* function minimiser, details unimportant */
double findminimum( double (*fptr)(double x, double y, void *ctx), void *ctx)
{
...
/* repeatedly make calls like this */
temp = (*fptr)(testx, testy, ctx);
}
/* the function we are minimising, sums two cubics */
double *cubics(double x, double y, void *ctx)
{
double *coeffsx = ctx;
double *coeffsy = coeffx + 4;
return coeffsx[0] * x * x * x + coeffsx[1] * x * x + coeffsx[2] * x + coeffsx[3] +
coeffsy[0] * y * y * y + coeffsy[1] * y * y + coeffsy[2] * y + coeffsy[3];
}
void caller()
{
/* context, the coefficients of the cubics */
double coeffs[8] = {1, 2, 3, 4, 5, 6, 7, 8};
double min;
min = findminimum(cubics, coeffs);
}
コンテキストポインタを使用することは、余分なパラメータが、指し示された機能にハードコードされる必要はなく、使用グローバルを必要とすることを意味する。
ライブラリ関数qsort()
はこの規則に従わず、簡単な比較関数のための文脈なしに逃げることができます。しかし、もっと複雑なものについては、コンテキストポインタが不可欠になります。
も参照してください
前書き
char
とint
ように、関数はCの基本的な機能です。そのため、ポインタを宣言することができます。つまり、 関数を呼び出すためにどの関数を渡すことができます。あなたが持っていた場合たとえば、 graph()
グラフを表示する機能を、あなたはにグラフ化するためにどの機能渡すことができますgraph()
。
// A couple of external definitions to make the example clearer
extern unsigned int screenWidth;
extern void plotXY(double x, double y);
// The graph() function.
// Pass in the bounds: the minimum and maximum X and Y that should be plotted.
// Also pass in the actual function to plot.
void graph(double minX, double minY,
double maxX, double maxY,
???? *fn) { // See below for syntax
double stepX = (maxX - minX) / screenWidth;
for (double x=minX; x<maxX; x+=stepX) {
double y = fn(x); // Get y for this x by calling passed-in fn()
if (minY<=y && y<maxY) {
plotXY(x, y); // Plot calculated point
} // if
} for
} // graph(minX, minY, maxX, maxY, fn)
使用法
したがって、上記のコードは、あなたが渡した関数をグラフにします - その関数が特定の基準を満たしている限り、つまりdouble
インを渡してdouble
アウトします。 sin()
、 cos()
、 tan()
、 exp()
など多くの関数がありますが、 graph()
自体のような多くの関数はありません!
構文
では、どの関数をgraph()
渡すことができないのか、どの関数を渡せないのかをどのように指定しますか?従来の方法は、読みやすく理解しにくい構文を使用することです。
double (*fn)(double); // fn is a pointer-to-function that takes a double and returns one
上記の問題は、同時に定義しようとする2つのことがあります:関数の構造とそれがポインタであるという事実です。したがって、2つの定義を分割してください!しかし、 typedef
を使うことで、(読みやすく理解しやすい)より良い構文を実現することができます。
関数ポインタを書くためのニーモニック
すべてのC関数は、実際には、コードが存在するプログラムメモリ内の場所へのポインタです。関数ポインタの主な用途は、他の関数に "コールバック"を提供すること(またはクラスとオブジェクトをシミュレートすること)です。
このページでさらに定義されている関数の構文は次のとおりです。
returnType(* name)(parameters)
関数ポインタ定義を書くためのニーモニックは、次の手順です。
- 通常の関数宣言を書くことから始め
returnType name(parameters)
:returnType name(parameters)
- ポインタの構文で関数名をラップします:
returnType (*name)(parameters)
基本
int 、 char 、 float 、 array / string 、 structなどへのポインタを持つことができるように、関数へのポインタを持つことができます。
ポインタの宣言 は、関数の戻り値、関数の名前 、引数の型/パラメータを受け取ります。
次の関数を宣言して初期化したとします:
int addInt(int n, int m){
return n+m;
}
この関数へのポインタを宣言して初期化することができます:
int (*functionPtrAdd)(int, int) = addInt; // or &addInt - the & is optional
void関数がある場合、次のようになります。
void Print(void){
printf("look ma' - no hands, only pointers!\n");
}
ポインタを宣言すると、次のようになります。
void (*functionPtrPrint)(void) = Print;
関数自体にアクセスするには、ポインタの逆参照が必要です。
sum = (*functionPtrAdd)(2, 3); //will assign 5 to sum
(*functionPtrPrint)(); //will print the text in Print function
このドキュメントのより高度な例に見られるように、関数へのポインタを宣言することは、関数がいくつかのパラメータよりも多く渡されると乱雑になる可能性があります。同じ構造体(戻り値の型と同じ型のパラメータ)を持つ関数へのポインタがいくつかある場合は、 typedefコマンドを使用して入力を省き、コードをより明確にすることをお勧めします。
typedef int (*ptrInt)(int, int);
int Add(int i, int j){
return i+j;
}
int Multiply(int i, int j){
return i*j;
}
int main()
{
ptrInt ptr1 = Add;
ptrInt ptr2 = Multiply;
printf("%d\n", (*ptr1)(2,3)); //will print 5
printf("%d\n", (*ptr2)(2,3)); //will print 6
return 0;
}
関数ポインタの配列を作成することもできます 。すべてのポインタが同じ "構造"である場合:
int (*array[2]) (int x, int y); // can hold 2 function pointers
array[0] = Add;
array[1] = Multiply;
さまざまな型の関数ポインタの配列を定義することも可能ですが、特定の関数にアクセスする必要があるときには型変換が必要になります。あなたはここでもっと学ぶことができます 。