私はプログラミング言語のコースを受講していて、extern "C"
宣言について話している。
この宣言は、「CとC ++のインターフェイス」以外のより深いレベルでどのように機能しますか?これは、プログラムで行われるバインディングにもどのように影響しますか?
extern "C"
次の記号が壊れていない(装飾されていない)ことを確認するために使用されます。
例:
次のコードがファイルにあるとしましょうtest.cpp
:
extern "C" {
int foo() {
return 1;
}
}
int bar() {
return 1;
}
実行した場合gcc -c test.cpp -o test.o
シンボル名を見てください:
00000010 T _Z3barv
00000000 T foo
foo()
その名前を保持します。
CとC++の両方でコンパイルできる典型的な関数を見てみましょう。
int Add (int a, int b)
{
return a+b;
}
現在、Cでは、関数は内部的に「_Add」と呼ばれています。一方、C ++関数は、名前マングリングと呼ばれるシステムを使用して、内部的にまったく異なるものと呼ばれます。これは基本的に、異なるパラメーターを持つ同じ関数が異なる内部名を持つように関数に名前を付ける方法です。
したがって、Add()がadd.cで定義されていて、add.hにプロトタイプがある場合、C++ファイルにadd.hをインクルードしようとすると問題が発生します。C ++コードは、add.cの関数とは異なる名前の関数を探しているため、リンカーエラーが発生します。この問題を回避するには、次の方法でadd.cを含める必要があります。
extern "C"
{
#include "add.h"
}
これで、C++コードはC++名のマングルバージョンではなく_Addとリンクします。
これは、式の使用法の1つです。結論として、C ++プログラムで厳密にCであるコードを(includeステートメントまたはその他の手段を介して)コンパイルする必要がある場合は、extern"C"{...}宣言でラップする必要があります。
extern "C"でコードのブロックにフラグを立てると、Cスタイルのリンケージを使用するようにシステムに指示します。
これは主に、リンカーが名前を壊す方法に影響します。C ++スタイルの名前マングリング(演算子のオーバーロードをサポートするためにより複雑です)を使用する代わりに、リンカーから標準のCスタイルの名前を取得します。
extern "C"
関数のタイプも変更することに注意してください。下位レベルのものを変更するだけではありません。
extern "C" typedef void (*function_ptr_t)();
void foo();
int main() { function_ptr_t fptr = &foo; } // error!
のタイプは&foo
、typedefが指定するタイプと同じではありません(ただし、コードは一部のコンパイラーで受け入れられますが、すべてのコンパイラーで受け入れられるわけではありません)。
C ++では、関数の名前/シンボルは実際には別の名前に変更されているため、異なるクラス/名前空間が同じ署名の関数を持つことができます。Cでは、関数はすべてグローバルに定義されており、そのようなカスタマイズされた名前変更プロセスは必要ありません。
C ++とCを相互に通信させるために、「extern C」は、C規則を使用しないようにコンパイラーに指示します。
extern Cは、C++コンパイラによる名前マングリングに影響を与えます。これは、C ++コンパイラに名前をマングルしないようにする方法、またはCコンパイラと同じ方法で名前をマングルする方法です。これは、CとC++をインターフェースする方法です。
例として:
extern "C" void foo(int i);
関数をCモジュールに実装できますが、C++モジュールから呼び出すことはできます。
問題は、CモジュールにC++モジュールで定義されたC++関数(明らかにCはC ++クラスを使用できない)を呼び出そうとすると発生します。Cコンパイラはが好きではありませんextern "C"
。
したがって、これを使用する必要があります。
#ifdef __cplusplus
extern "C" {
#endif
void foo(int i);
#ifdef __cplusplus
}
#endif
これがヘッダーファイルに表示されると、CコンパイラとC ++コンパイラの両方が宣言に満足し、CまたはC ++モジュールのいずれかで定義できるようになり、CコードとC++コードの両方で呼び出すことができます。
extern "C"は、囲まれたコードがCスタイルのリンクと名前マングリングを使用することを示します。C ++は、より複雑な名前マングリング形式を使用します。次に例を示します。
http://en.wikipedia.org/wiki/Name_mangling
int example(int alpha, char beta);
Cで:_example
C ++の場合:__Z7exampleic
更新:GManNickGがコメントで指摘しているように、名前マングリングのパターンはコンパイラーに依存します。
extern "C"は、Cバインディングを使用して関数を宣言するためのキーワードです。これは、CコンパイラとC++コンパイラがソースをオブジェクトファイル内の異なる形式に変換するためです。
たとえば、コードスニペットは次のとおりです。
int _cdecl func1(void) {return 0}
int _stdcall func2(int) {return 0}
int _fastcall func3(void) {return 1}
32ビットCコンパイラは、次の形式でコードを変換します。
_func1
_func2@4
@func3@4
cdeclでは、func1は' _name 'として変換されます
stdcallでは、func2は' _name@X 'として変換されます
fastcallでは、func2は' @ name@X 'として変換されます
「X」は、パラメータリスト内のパラメータのバイト数を意味します。
Windowsでの64ビット規則には、先頭にアンダースコアはありません
C ++では、クラス、テンプレート、名前空間、および演算子のオーバーロードが導入されています。これは、同じ名前の2つの関数が許可されていないため、C++コンパイラがシンボル名で型情報を提供するためです。
たとえば、コードスニペットは次のとおりです。
int func(void) {return 1;}
int func(int) {return 0;}
int func_call(void) {int m=func(), n=func(0);}
C ++コンパイラは、コードを次のように変換します。
int func_v(void) {return 1;}
int func_i(int) {return 0;}
int func_call(void) {int m=_func_v(), n=_func_i(0);}
「_v」と「_i」は「void」と「int」の型情報です。
これがmsdnからの引用です
"externキーワードは、変数または関数を宣言し、外部リンケージがあることを指定します(その名前は、定義されているファイル以外のファイルから表示されます)。変数を変更する場合、externは、変数に静的期間があることを指定します(割り当てられます)。プログラムの開始時とプログラムの終了時の割り当て解除時)変数または関数は、別のソースファイルで定義することも、後で同じファイルで定義することもできます。ファイルスコープでの変数と関数の宣言は、デフォルトでは外部です。」
http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx