3

このコードは合法ですか?

extern "C" typedef void (ft_blah_c)();
/*extern "C++"*/ typedef void (ft_blah_cpp)();

extern "C" void fn_blah_c() {}
/*extern "C++"*/ void fn_blah_cpp() {}

ft_blah_c *g_Blah_c = fn_blah_cpp; // <--- ?
ft_blah_cpp *g_Blah_cpp = fn_blah_c; // <--- ?

同様の割り当てを持つ実際のコードがあり、問題なくコンパイルおよび実行されます(MSVC 2010)。

4

2 に答える 2

4

一般的に、それは機能しないはずです。問題は、呼び出すfn_blah_cfn_blah_cpp直接呼び出すと、コンパイラは使用する関数と呼び出し規約を知っていますが、それらを関数ポインタに格納すると、コンパイラはそのポインタのみを認識し、関数ポインタのタイプのみを使用して方法を決定できることです。引数と戻り型を渡します。

CとC++の呼び出し規約がご使用の環境で同じである場合、それは機能します(そして、おそらくコンパイラーがそれを許可している理由です)が、一般的にはそうではなく、割り当ては失敗するはずです。

于 2012-12-29T11:52:58.147 に答える
3

いいえ、これは合法ではありません。関数型間の変換を暗黙的に行うことはできません。

関数型間で明示的にキャストすることは合法です。

ft_blah_c *g_Blah_c = reinterpret_cast<ft_blah_c>(fn_blah_cpp);
ft_blah_cpp *g_Blah_cpp = reinterpret_cast<ft_blah_cpp>(fn_blah_c);

ただし、実際に異なる型の関数ポインターを介して関数を呼び出すと、未定義の動作になります。関数を呼び出す前に、元の型に戻す必要があります。

この根本的な理由は、標準の範囲外ですが、関数を呼び出すための機械命令が関数の言語リンケージに基づいて異なる可能性があるためです。これは「呼び出し規則」と呼ばれ、引数と戻り値の受け渡し規則、関数のプロローグとエピローグなど、多くの詳細が含まれます。

たとえば、C 言語リンケージを持つ関数は、引数がスタック上で見つかることを期待する場合がありますが、C++ 言語リンケージを持つ関数は、引数がレジスターで渡されることを期待する場合があります。呼び出し元のコードが正しいリンケージを認識していない場合、引数データを 1 つの場所に配置し、関数は別の場所を調べてゴミのみを読み取るため、正しく動作しません。

于 2013-02-27T19:18:03.270 に答える