web-dev-qa-db-ja.com

Cでvoidポインターをキャストする必要があるのはいつですか?

私はMitchell、Oldham、SamuelによるAdvanced Linux Programmingを見てきました。私はpthreadのセクションで、私を混乱させるvoidポインターとキャストについて何かを見てきました。

引数をpthread_create()に渡すと、関数が期待しているものであっても、ポインターをvoidポインターにキャストしません。

pthread_create( &thread, NULL, &compute_prime, &which_prime );

ここでは、which_primeintタイプです。

しかし、pthread_joinを使用してスレッドから返された値を取得すると、変数はvoidポインターにキャストされます。

pthread_join( thread, (void*) &prime );

ここでは、primeはタイプintです。

キャストが最初のインスタンスではなく2番目のインスタンスで行われるのはなぜですか?

20
Amoeba

2番目の例は、void*へのキャストが通常間違いである理由の良い例です。そのはず

void *primep = ′  // no cast needed
pthread_join(thread, &primep);

pthread_joinは2番目の引数としてvoid**を取るためです。 void*は、void*void**に自動的に変換されるため、バグがコンパイラを通過することを確認するだけです。

したがって、doの場合は、void*にキャストするか、戻す必要があります。

  • 整数((u)intptr_t)として格納されたポインタを使用する場合。
  • 不完全なプロトタイプを持つ関数にポインタを渡し、void*を取得する場合(または別のタイプのポインタを取得し、void*を取得する場合)。これは通常、printfなどの可変数の引数を取る関数を意味します。
10
Fred Foo

Cのvoidへのポインタから、またはポインタへのキャストは不要です。

6.3.2.3ポインター

1 voidへのポインタは、不完全型またはオブジェクト型へのポインタとの間で変換されます。不完全またはオブジェクト型へのポインタは、voidへのポインタに変換され、再び元に戻されます。結果は、元のポインタと同じになります。


これの唯一の例外は

  • "%p"に対してのみ定義されているため、void *変換指定子を使用してポインターを出力する場合。
  • intptr_tまたはuintptr_tからvoid *にポインターの値をコピーする場合。
9
alk

Cでは、任意のポインター型およびその逆からvoid *へのキャストは暗黙的に行われます。 2番目の例ではキャストの必要はありません。

(C++では、void *へのポインターのキャストも暗黙的に行われます(ただし、void *にキャストできない関数ポインターおよび関数メンバー/メソッドポインターを除く)。ただし、キャストバックには明示的なキャストが必要です。)

4
Asaf

ドキュメント、

int pthread_join(pthread_t thread, void **retval);

したがって、pthread_joinは2番目の引数としてpointer to void*を取ります。それの訳は、

Pthread_joinでは、終了したスレッドによってpthread_exitに渡されたアドレスを取得します。単純なポインターだけを渡す場合、値によって渡されるため、ポインターが指している場所を変更できません。 pthread_joinに渡されるポインターの値を変更できるようにするには、それ自体をポインターとして、つまりポインターへのポインターとして渡す必要があります。

さて、あなたの質問に、「キャストが最初のインスタンスではなく2番目のインスタンスで行われるのはなぜですか?」最初のインスタンス、つまりpthread_createでは、 void*を4番目の引数として使用します。したがって、&which_primeを渡すと、暗黙的にvoid*に変換されます。

2番目のインスタンス、つまりpthread_joinでは、void**が必要であり、そこに&primeを渡します。したがって、コンパイラは文句を言うでしょう。したがって、バグを回避するために、作成者はvoid*のキャストを渡します。キャストは自動的にvoid**に変換されます。

しかし、これは良い解決策ではありません。

ソリューション::

void* prime ; // make prime as void*
pthread_join( thread, &prime );
printf( "%" PRIxPTR "\n", (intptr_t)prime ) ; 
// intptr_t instead of int to get an integer type 
// that's the same size as a pointer
2
Abhineet

同じコードが 参照 の他の 質問 であると思います。

2番目のリンクの答えは次のとおりです。

無効です。多くのシステムで発生するsizeof(int)== sizeof(void *)の場合、単純に機能します。

Void *は、データオブジェクトへのポインタを保持できることが保証されているだけです。

これは件名のC FAQです。

そして引用されたテキスト:

整数はどのようにポインタとの間で変換されますか?一時的に整数をポインターに詰め込むことはできますか?

ポインターから整数への変換および整数からポインターへの変換は実装定義であり(質問11.33を参照)、ポインターを変更せずに整数に変換して戻すことができるという保証はありません。

ポインターを整数に、または整数をポインターに強制することは、決して良い習慣ではありませんでした

1
user1508519