6
int foo(char *c)  {...}

main() {
     int (*thud)(void *);

     thud = (int (*)(void *))(foo);
}

課題の評価では、実際に何が行われますか?

fooキャスト型と;には違いがあります。キャスト型はポインタfooあり、関数です。では、コンパイラは ' (foo)' の内容をポインタに変換しfooてからキャストを行うのでしょうか? 他に意味がないように見えるからです。もう1つのオプションは、関数自体が を取得してを返す関数へのポインターに変換されることです。私が知る限り、関数はメモリ内のコードのラベルであるため、ポインターになることはできません。変数。void*int

4

5 に答える 5

5

Cでは、絶対に何もありません。ばかげたことをするのを防ぐのは、単なるコンパイラの接着剤です。C では、呼び出し元がスタック フレームを維持する責任があるため、関数を呼び出すときにキャストが必要です (つまり、引数と戻り値がスタックにプッシュされます)。呼び出し元のスタックが不適切に変更される可能性が低いため、これにより safe(r) になります。ただし、呼び出された関数は、特定のまれなケースで呼び出し元のスタックを混乱させる可能性があります。

割り当てが関数ポインターをコピーすることを明確にする必要があります。しかし、C では、すべての関数ポインターは単なるポインターです。型とキャストはすべてコンパイラの接着剤です。

別の明確化: 標準では、(6.5.2.2 で) 呼び出し元が互換性のない型を使用する場合の動作は未定義であると指定されています。たとえば、void を返す関数を int を返す関数にキャストしてからその関数を呼び出すと、「返された」値は無意味になります。関数を呼び出す前に、関数を互換性のある型にキャストすることをお勧めします。そうしないと、予期しない結果が生じる可能性があります。

于 2009-04-16T18:31:52.077 に答える
5

関数の名前は、そのように使用される場合はポインターです。これは、配列の名前がその最初の要素へのポインタであることにいくらか似ています。

そうは言っても、関数の実際のプロトタイプとは異なる型のポインターを介して関数を呼び出すことは (例のように) 未定義の動作です。やらないでください。

補遺

変換されたポインターを使用して、ポイント先の型と互換性のない型を持つ関数を呼び出す場合、動作は未定義です。

C 標準のセクション 6.3.2.3から。

于 2009-04-16T18:32:27.877 に答える
2

これは、Rick C. Petty の回答へのコメントになる予定でしたが、300 文字には収まりません。

C 標準はそれほど制限的ではありません。オブジェクトへのポインター (および関数はオブジェクトではありません) は、問題なく「void へのポインター」に変換して元に戻すことができます。POSIX では、関数へのポインターはすべて同じサイズであり、void へのポインターに変換できる必要があります。

POSIX 2008 - 一般情報 - コンパイル環境

2.12.3 ポインタ型

すべての関数ポインタ型は、void への型ポインタと同じ表現を持つものとします。関数ポインターを void * に変換しても、表現は変更されません。このような変換の結果の void * 値は、情報を失うことなく、明示的なキャストを使用して元の関数ポインター型に戻すことができます。

注: ISO C 標準ではこれは必要ありませんが、POSIX 準拠のために必要です。

于 2009-04-16T19:35:23.623 に答える
2

C のポインタはアドレス、つまり、ある場所に格納された数値です。C の関数は、何らかのコードへのアドレスです。それらの 2 つは同じものの 1 つです。

于 2009-04-16T18:32:44.227 に答える
2

正しい用語は減衰です。関数は、キャスト前のfooポインターに減衰します。fooキャスト自体は、考えられるすべてのプラットフォームでノーオペレーションになります。

ただし、そのようなキャストを含むプログラムの動作は、C 標準では定義されていないことに注意してください。

于 2009-04-16T18:34:03.317 に答える