11

任意の関数を安全に指すことができる関数ポインタを宣言するにはどうすればよいですか?

voidポインタのようなものが必要ですが、関数用です。単純にそれを使用することを考えましたが、この質問への回答によると、少なくとも非POSIXシステムでは、関数ポインターをデータポインターに確実に変換することはできません。

私はこれのいくつかの可能な例を検索して見つけました:

  • WindowsにはFARPROCがあります:

    int (FAR WINAPI * FARPROC) ()
    

    ドキュメントにもこれが記載されています:

    Cでは、FARPROC宣言は、パラメーターリストが指定されていないコールバック関数を示します。

    うまく聞こえますが、戻り値はどうですか?として明確に指定されていintます。

  • OpenGLの仕様の1つは次のとおりです。

    typedef void (*GLfunction)();
    extern GLfunction glXGetProcAddressARB(const GLubyte *procName);
    

    パラメータリストも指定されていませんが、リターンタイプは明確に指定されていvoidます。

これをどう解釈するかわかりません。関数ポインターは、使用される前に、適切なプロトタイプを使用して関数ポインター型にキャストされます。例えば:

typedef void (*function_pointer_t)();
extern function_pointer_t get_function(const char * name);

/* Somewhere else... */

typedef double (*cosine_function_t)(double);
cosine_function_t cosine = (cosine_function_t) get_function("cos");

このコードは安全ですか?それはC標準に違反していますか、それとも未定義の動作を引き起こしますか?

4

2 に答える 2

13

まず、キャストなしで任意の関数ポインター型を受け入れることができる関数ポインターを宣言する方法はありません。すでにお気づきのように、問題は、関数ポインタ宣言がすぐに特定の戻り型を想定することです。したがって、void *関数ポインタの世界に完全に類似したものはありません。

次に、明示的なキャストを使用して変換を強制する限り、任意の関数ポインターを使用して他の関数ポインター値を格納できます。もちろん、別の型に強制的に変換されたポインタを介して適切な呼び出しを実行するには、元の型に戻す必要があります。つまり、呼び出しの時点で適切な関数ポインタ型が復元されている限り、これによって未定義の動作が発生することはありません。

あなたの例では、によって返されるポインタがget_function実際にdouble (double)型の関数を指している場合、ポインタを介してその関数を呼び出すことは完全に安全cosineです。ポインタ値がポインタの中間に格納されているという事実は、void (*)()それを破壊しません。

第三に、C言語では、()パラメーター宣言は不特定の数とタイプのパラメーターを表します。これは、キャストなしで使用できる「ユニバーサル」関数ポインター型の方向へのステップです(呼び出しで適切なパラメーターを指定し、戻り型が一致する限り)

void foo(void);
void bar(int i);
void baz(long i, double x);

int main() {
  void (*a[3])() = { foo, bar, baz };
  a[0]();
  a[1](42);
  a[2](5L, 3.1415);
}

しかし、繰り返しになりますが、リターンタイプの問題はまだ残っています。

于 2012-10-06T19:26:58.553 に答える
5

ユニオンを使用できます。確かに、このポインターを介して呼び出す関数型の数は有限であり、それらすべてを結合することができます。

于 2012-10-06T19:48:24.810 に答える