1

次のようなvoid関数を使用してPOSIXスレッドを作成しようとしました。

void thread_func(void* p_Arg)
{
    printf("Hello, World!\n");
}

int main(void)
{
    pthread_t thread;
    pthread_create(&thread, NULL, (void*)thread_func, (void*)NULL);

    return 0;
}

コードは正常に機能します!しかし、この場合、thread_funcをvoid *にキャストしても安全ですか?

4

3 に答える 3

3

いいえ。コードはIA64でクラッシュします。関数ポインタのキャストは、発生するのを待っているバグです。正しい署名を使用して、0などのダミー値を返すだけです。

void (*)(void*)また、関数ポインタ(のような)をオブジェクトポインタ(のような)にキャストすることも、潜在的に危険な操作であることに注意してくださいvoid*。C標準は、オブジェクトポインタと関数ポインタが同じ表現を持つことを保証しないためです。異なる表現を使用するアーキテクチャを手元に置いていることはわかりませんが、関数ポインタに、オブジェクトポインタには通常存在しない余分なコンテキストビットが含まれている可能性は確かにあります。

したがって、明示的に許可されている実装を使用している場合を除いて、関数ポインタをオブジェクトポインタにキャストしないでください。たとえば、POSIXシステムではこれが必要であるため、からの戻り値をdlsym(3)オブジェクトポインタから関数ポインタに安全にキャストできます。 。

于 2012-08-16T17:31:34.310 に答える
2

この場合、thread_funcをvoid*にキャストしても安全ですか

これはおそらく多くのプラットフォームで機能しますが、次のことを知っておく必要があります。

  • Avoid *は、関数ポインタではなく、オブジェクトポインタのみを格納できることが保証されています。
  • スレッド関数が返されるはずvoid *ですが、それを回避する完全に合法的な方法はありません。pthread_joinたとえば、NULL以外の結果が出た場合はどうなるのでしょうか。

voidへのポインタは、任意のオブジェクト型へのポインタまたはポインタから変換できます。任意のオブジェクトタイプへのポインタは、voidへのポインタに変換されて再び戻る可能性があります。結果は元のポインタと同じになります。

そしてもちろん:

あるタイプの関数へのポインターは、別のタイプの関数へのポインターに変換され、また元に戻される場合があります。結果は元のポインタと同じになります。変換されたポインターを使用して、参照されている型と互換性のない型の関数を呼び出す場合、動作は定義されていません。

于 2012-08-16T17:31:31.737 に答える
0

void *Cコンパイラは、危険なポインタ変換を暗黙的に実行する場合、従来は(C ++コンパイラよりも)寛容ですが、Cコンパイラを使用すると、大量の診断を生成せずに関数ポインタパラメータの引数を指定できるとは信じられません。メッセージ。万が一、を含めることを忘れなかったpthread.hので、C99より前のコンパイラに関数プロトタイプに依存する代わりに引数の型を推測させましたpthread_createか?

いずれにせよ、本当にこのハックに依存したいのであれば、少なくともポインタを適切なターゲットタイプにキャストしてくださいvoid *(*)(void*)void *

pthread_create(&thread, NULL, (void *(*)(void*)) thread_func, NULL);

ただし、C言語の観点からは決して安全ではありません。POSIXは、それを「より安全」にすることができる追加の保証を提供することができますが、とにかくそれをしません。

スレッド関数をvoid *return型で適切に宣言し、この役に立たないハックを取り除きます。

于 2012-08-16T17:42:32.733 に答える