関数ポインタ


関数ポインタ


関数ポインタは関数のアドレスを保持するポインタである.関数へのポインターの能力は、Cの重要で有用な機能であることがわかります.これは、コンパイル時には知られていないかもしれないし、条件文を使用せずに、関数を実行する別の方法を提供します.

関数ポインタの宣言


簡単な宣言から始めましょう.以下では、voidに渡されvoidを返す関数へのポインタを宣言します.
void (*foo)();
関数ポインタが使用されている場合、プログラマは適切なパラメータが渡されたかどうかをチェックしないので、適切に使用されるように注意しなければなりません.
その他の関数ポインタ宣言例
int (*f1)(double);          // Passed double and returns an int

void (*f2)(char*);          // Passed a pointer to char and returns void

double* (*f3)(int, int);    // Passed two integers and returns a pointer to a double

関数ポインタの使用


int (*fptr1)(int);

int square(int num) {
    return num * num;
}
関数ポインタを使用して四角関数を実行するには、関数のポインタに四角関数のアドレスを代入する必要があります.
int n = 5;
fprt1 = square;
printf("%d  squared is %d\n", n, fptr1(n));
出力:
5 squared is 25
配列名と同様に、関数の名前を単独で使用する場合は、関数のアドレスを返します.

関数ポインタのtypedef


関数ポインターの型定義を宣言するのは便利です.
typedef int (*funcptr)(int);
funcptr fptr2;

fptr2 = square;
printf("%d  squared is %d\n", n, fptr2(n));
タイプ定義は少し奇妙に見えます.通常、型定義の名前は宣言の最後の要素です.

関数ポインタを渡す


関数ポインタを渡すのは簡単です.単に関数のパラメーターとして関数ポインター宣言を使用します.以下に宣言されているように、add、sub、およびcompute関数を使用して関数ポインタを渡します.
int add(int num1, int num2) {
    return num1 + num2;
}

int sub(int num1, int num2) {
    return num1 - num2;
}

typedef int (*fptrOperation)(int, int);

int compute(fptrOperation operation, int num1, int num2) {
    return operation(num1, num2);
}
printf("%d\n", compute(add, 6, 5));
printf("%d\n", compute(sub, 6, 5));
出力:
11
1
add関数とsub関数のアドレスは、Compute関数に渡されました.これらのアドレスはそれから対応する操作を呼び出すのに使用されました.この例では、関数ポインターを使用してコードをより柔軟に行う方法も示します.

関数ポインタの復帰


関数ポインタを返すには、関数の戻り値の型を関数ポインタとして宣言する必要があります.
次のselect関数を使用して、文字入力に基づく操作への関数ポインタを返します.渡されるopcodeに応じて、add関数またはsub関数のいずれかへのポインタを返します.
fptrOperation select(char opcode) {
    switch(opcode) {
        case '+': return add;
        case '-': return sub;
    }
}

int evaluate(char opcode, int num1, int num2) {
    fptrOperation operation = select(opcode);
    return operation(num1, num2);
}
printf("%d\n", evaluate('+', 6, 5));
printf("%d\n", evaluate('-', 6, 5));
出力:
11
1
評価関数はこれらの関数を結びつける.この関数は、実行する演算を表す2つの整数と文字を渡します.この関数は、選択した関数にオペコードを渡し、実行する関数へのポインタを返します.return文では、この関数を実行し、結果を返します.

関数ポインタの配列


関数ポインタの配列を使用して、いくつかの基準に基づいて評価する関数を選択できます.このような配列を宣言するのは簡単です.以下に示すように、関数ポインタ宣言を配列の型として使用します.配列もすべてのNULLに初期化されます.初期化値のブロックが配列で使用される場合、その値は配列の連続した要素に割り当てられます.値の数が配列のサイズより小さい場合、値は配列のすべての要素を初期化するために使用されます.
typedef int (*operation)(int, int);
operation operations[128] = {NULL};
また、以下のようにtypedefを使わずにこの配列を宣言できます:
int (*operations[128])(int, int) = {NULL};
この配列の意図は、文字インデックスが実行する対応する関数を選択できるようにすることです.例えば、'*'文字が存在する場合、乗算関数を識別します.文字リテラルが整数であるため、文字インデックスを使用できます.128要素は最初の128 ASCII文字に対応します.
配列を全てのヌルに初期化したあと、プラスとマイナスのサインに対応する要素にadd関数とsub関数を割り当てます.
void initializeOperationsArray() {
    operations['+'] = add;
    operations['-'] = sub;
}
評価関数は評価関数として書き換えられる.関数ポインタを取得するためにselect関数を呼び出す代わりに、操作文字をインデックスとして操作を使用しました.
int evaluateArray(char opcode, int num1, int num2) {
    fptrOperation operation;
    operation = operations[opcode];
    return operation(num1, num2);
}
initializeOperationsArray();
printf("%d\n", evaluateArray('+', 6, 5));
printf("%d\n", evaluateArray('-', 6, 5));
出力:
11
1
評価関数のより堅牢なバージョンでは、関数を実行しようとする前にNULL関数ポインタをチェックします.

関数ポインタの比較


関数ポインタは、等値と不等式演算子を使用して、互いに比較することができます.
fptrOperation fptr1 = add;

if(fptr1 == add) {
    printf("fptr1 points to add function\n");
}
else {
    printf("fptr1 doesn't point to add function\n");
}
機能ポインタの比較が有用であるところのより現実的な例は、仕事のステップを表示する機能ポインタのアレーを含む.たとえば、在庫部分の配列を操作する一連の関数を持つことができます.操作の1セットは、部品をソートして、それらの量の累積総和を計算して、それから配列および合計を表示することであってもよい.操作の第2のセットは、アレーを表示することになっていて、最も高価で最も高価であるとわかることができて、それからそれらの差を表示できる.各操作は、個々の関数へのポインタの配列で定義できます.両方のリストにログ操作が存在する可能性があります.つの機能ポインタを比較する能力は、ログからの操作を削除することによって操作の動的変更を許可する.そして、リストから機能を見つけることによって、そして、それから除去する.

キャスティング機能ポインタ


つの関数へのポインターを別の型にキャストできます.これは、ランタイムシステムが関数ポインターによって使用されるパラメーターが正しいことを確認していないため、注意して行う必要があります.また、関数ポインタを別の関数ポインタにキャストし、それからバックすることも可能です.結果のポインタは元のポインタに等しくなります.関数ポインタのサイズは必ずしも同じではありません.
typedef int (*fptrToSingleInt)(int);
typedef int (*fptrToTwoInts)(int,int);
int add(int, int);

fptrToTwoInts fptrFirst = add;
fptrToSingleInt fptrSecond = (fptrToSingleInt)fptrFirst;
fptrFirst = (fptrToTwoInts)fptrSecond;
printf("%d\n",fptrFirst(6,5));
出力:
11
関数ポインターとデータへのポインターの間の変換は、動作するように保証されません.

関数ポインタによるvoid *


の使用void* 関数ポインタで動作することは保証されません.つまり、以下のように関数ポインタをvoid *に割り当てるべきではありません.
void* pv = add;
しかし、関数ポインタを交換するときには、以下のように「基本」関数ポインタ型を見ることが一般的です.これは関数への関数ポインタとしてfptrbaseを宣言します.
typedef void (*fptrBase)();
fptrBase basePointer;
fptrFirst = add;
basePointer = (fptrToSingleInt)fptrFirst;
fptrFirst = (fptrToTwoInts)basePointer;
printf("%d\n",fptrFirst(6,5));
ベースポインタは関数ポインタの値を交換するプレースホルダとして使用されます.
関数ポインタの正しい引数リストを必ず使用してください.そうすることができないことは、不確定な行動に終わります.