次の 2 つは同じですが、C99 標準void *
では関数ポインタへのキャストが定義されていません。
誰かが2番目の仕組みを説明できますか? 少し紛らわしいです!
int (*fptr)(int);
fptr = (int (*)(int))dlsym(handle, "my_function");
int (*fptr)(int);
*(void **) (&fptr) = dlsym(handle, "my_function");
次の 2 つは同じですが、C99 標準void *
では関数ポインタへのキャストが定義されていません。
誰かが2番目の仕組みを説明できますか? 少し紛らわしいです!
int (*fptr)(int);
fptr = (int (*)(int))dlsym(handle, "my_function");
int (*fptr)(int);
*(void **) (&fptr) = dlsym(handle, "my_function");
最初の行 (2 番目のコードの貼り付け) で、関数ポインターを宣言します (ご存知だと思います)。
さて、dlsym(3)
を返す呼び出しですvoid *
。
したがって、2 行目は次のように読むこともできます。
*((void **) (&fptr)) = dlsym(handle, "function");
そうでなければ、関数の結果を としてキャストしint (*)(int)
、与えられた結果を fptr に影響を与える代わりに。fptr にポインターをキャストします (または、fptr のアドレス: ポインター上のポインターをキャストします) void**
。次に、このポインターを逆参照し、効果的に fptr (元のものと同じですが、int (*)(int)
型はありません) を提供し、呼び出しの結果を取得しdlsym
ます。これは、型の不一致に関する警告/エラーをトリガーしないようにコンパイラを「だます」方法にすぎません。また、選択した構文が好みの問題であっても、リリースするプログラムで使用する前に十分に理解しておく必要があることに注意してください。
役に立てば幸いです;)
違いは次のものと同じです。
float f = 3.14;
int i = (int)f;
と
float f = 3.14;
int i = *(int*)&f;
1 つ目は、値の通常のキャストです。場合によっては (int <--> float、または 8086 日後のニア ポインター <--> ファー ポインター)、変換が発生します。場合によっては (関数ポインターと通常のポインターの間の一部のキャスト)、コンパイルさえしません。
2 つ目は生のビット単位のコピーで、コンパイラは常にこれを受け入れますが、変換をバイパスし、変数を異なるサイズの別の変数に書き込む可能性があります。特に関数ポインタでは非常に危険です。
2 番目のバージョンが合法である理由は、ISO 規格の次の部分にあります。
6.3.2.3 ポインター 1 void へのポインターは、任意のオブジェクト型へのポインターとの間で変換できます。任意のオブジェクト型へのポインターは、void へのポインターに変換され、再び元に戻される可能性があります。結果は元のポインタと等しくなります。
(**void)
、オブジェクトへのポインターであるだけでなく&fptr
(&fptr は関数へのポインターへのポインターであるため)、2 番目の式のキャストは上記のステートメントによって明示的に許可されます。あなたの最初の式は、オブジェクトへのポインター(むしろvoid
標準では特殊なケースであるポインター)を関数へのポインター(オブジェクトではない)に変換しようとしていますが、これはあなたが指摘したように標準では許可されていませんアウト。