web-dev-qa-db-ja.com

関数ポインターを渡す

ポインター関数にポインターを渡す方法については少し混乱しています。問題なく理解している関数(ExecBlock)へのポインターを取る関数があります。しかし、逆参照されたポインター(正確には何なのかわかりません)を受け取り、渡された関数に引数がある場合は引数を取る関数(ExecBlock2)の別のプロトタイプが与えられます。誰かが優先順位と、ポインター関数の逆参照が何をするかを正確に説明できたら。関数自体を渡すだけではありませんか?この場合、(void *)は何をしますか?

int ExecBlock (void (*f) (void), int isSet)
{
    return ExecBlock2( HOW TO PASS HERE? , NULL, isSet);
}

int ExecBlock2(void *(*f)(void *), void *arg, int isSet)
{
    ... more code
}
18
ThePedestrian
_void (*f) (void)
_

voidを返す引数なしの関数へのポインタを意味します。

_void *(*f)(void *)
_

voidポインターを取り、voidポインターを返す関数へのポインターを意味します。

型が異なるため、コンパイラはキャストせずに一方を他方に渡すことを許可しません。 (ここで、キャストは実際には正しい答えではないことに注意してください。@ detlyが指摘しているように、結果は未定義の動作になります。)

関数へのポインターの逆参照に関しては、関数ポインターを呼び出すために明示的に「*」を置く必要はありません。たとえば、次のようにするだけで関数ポインタfを呼び出すことができます。

_f();
_

関数ポインターの例

関数fがあり、_takes_a_function_という関数に渡したいとします。 _takes_a_function_はおそらく次のような型を持ちます

_void takes_a_function(void (*f)(void *data), void *data);
_

_takes_a_function_への2つの引数、関数ポインター、およびデータへのvoidポインターがあることに注意してください。また、関数fは引数としてvoidポインターを受け取ることに注意してください。考えは、データを_takes_a_function_に渡すことができ、fに渡すことです。たとえば、_takes_a_function_は次のように定義できます。

_void takes_a_function(void (*f)(void *), void *data) {
  f(data);
}
_

それでは、_takes_a_function_に渡す関数を書きましょう。この関数は、渡されたintを出力するだけです。

_void prints_an_int(void *data) {
  // The idiom for converting a void pointer to another kind
  // of pointer.  NO NEED TO CAST.  Note this behavior is only
  // defined if the pointer data really does point to an int.
  int *i = data;
  printf("%d", *i);
}

int i = 0;
takes_a_function(prints_an_int, &i);
_

この例に関するいくつかの重要なポイント:

  • _prints_an_int_は、_takes_a_function_が期待する関数ポインターと同じ型を持っています。キャストする必要はありません。
  • 関数への参照を作成するために_&_演算子を使用する必要はありません。これが、_prints_an_int_を_takes_a_function_に直接渡すことができる理由です。しかし、takes_a_function(&prints_an_int, &i)と言うこともできますが、それは同じです。
  • _void*_は、基本的に「不明な型へのポインター」を意味します。実際にそれを行うには、タイプ_void*_の変数を、期待するタイプの別のポインター変数に割り当てる必要があります。これは、実際に正しいポインター型を渡した場合にのみ機能することが保証されています!この例では、データが実際にintを指しているため、dataを_int*_に割り当てることができます。単なる整数よりも多くのデータが必要な場合、一般的なパターンは、必要なすべてのフィールドを含む独自の構造体型を作成し、代わりに渡すことです。
  • 特別な場合として、コンパイラーは、voidポインターを他のポインターに割り当てたり、その逆を行ったりするときにキャストする必要はありません。しかし、繰り返しますが、最終的にvoidポインターを正しい型に変換し直した場合にのみ、定義された動作が得られます。
37
Chris Rice

たとえば、あなたは機能を持っています

void * abc(void *)
{
//... 
}

int ExecBlock (void (*f) (void), int isSet)
    {
        return ExecBlock2( abc , NULL, isSet); //use name of the function which have void * as parameter type list and return type  void *   
        }

int ExecBlock2(void *(*f)(void *), void *arg, int isSet)
{
    ... more code
}  

function-pointers を参照してください

1
Gangadhar