簡単に言うと、extern "C" は、C++ が C との互換性を保つために C 言語のシンボルを宣言または定義する方法です。言うのは簡単ですが、理解するにはまだ少し時間がかかります。まず、C++ と C の違いから始めなければなりません。
シンボル
コードから実行可能プログラムまで、コンパイルとリンクという 2 つのプロセスを経る必要があることは誰もが知っています。コンパイル フェーズでは、構文検出とコード拡張も行われます。変数をシンボルに変換すると、リンク時に実際にはシンボルを介して配置されます。コンパイラーが C および C++ コードをコンパイルする場合、変数をシンボルに変換するプロセスは異なります。この記事で使用するコンパイラは gcc4.4.7 です
まず簡単なコードを見てみましょう
/* hello.c */
#include <stdio.h>
const char* g_prefix = "hello ";
void hello(const char* name)
{
printf("%s%s", g_prefix, name);
}
ログイン後にコピー
ここでのファイル名は hello.c であることに注意してください。 gcc -c hello.c を実行してコンパイルし、ターゲット ファイル hello を取得します。 o. Linux で nm を使用してターゲット ファイルのシンボル テーブルを表示すると、次の結果が得られます ($ 記号はシェル コマンド プロンプトを表します)
$ nm hello.o
0000000000000000 D g_prefix
0000000000000000 T hello
U printf
ログイン後にコピー
これは C コードのコンパイル済みシンボル リストで、その 3 番目の列は次のとおりです。コンパイルされたシンボル名を主に調べます。自己定義のグローバル変数 g_prefix と関数 hello のコンパイルされたシンボル名は、コード内のものと同じです。 hello.c の名前を hello.cpp に変更し、gcc -c hello.cpp を再コンパイルして hello.o を取得し、nm を使用して表示します。結果は次のとおりです
0000000000000000 T _Z5helloPKc
U __gxx_personality_v0
0000000000000000 D g_prefix
U printf
ログイン後にコピー
これは、C++ コードがコンパイルされた後のシンボル リストです (gcc)この時点では、g_prefix のシンボルは変更されていませんが、関数 hello のシンボルが _Z5helloPKc に変更されていることがわかります。これは、gcc が C と C++ のコードを処理していることを示しています。 C++ コードの場合は異なります。C コードの場合、変数のシンボリック名は変数そのものです (初期の頃は、コンパイラーは C コード変数の前にアンダースコア _ を追加していましたが、現在はデフォルトで追加しません。コンパイル時のコンパイル オプション -fno-leading-underscore および -fleading-underscore)、C++ コードの場合、それがデータ変数でネストされていない場合、変数名がネストされている場合はシンボル名自体になります。 (名前空間またはクラス内) または関数名である場合、シンボル名は次の規則に従います。
1 を処理するには、シンボルは _Z
2 で始まり、ネストがある場合は、その後に N が続きます。名前空間、クラス、関数の名前は長さであり、 E
3 で終わります。ネストがない場合は、直接名前の長さの後に名前が続きます
4。パラメータリスト、型とシンボルの対応関係は以下の通りです
int -> i
float -> f
double -> d
char -> c
void -> v
const -> K
* -> P
ログイン後にコピー
こうすることで、コンパイル後の C++ コードで void hello (const char*) が発生する理由が簡単に理解できます シンボルは _Z5helloPKc (PKc は右から左に型に変換されます) char const * (コンパイラの内部表現)、私たちが慣れている表現は const char* (同じです)、c++filt ツールはシンボルから名前を逆にすることができます。使用方法は c です。 ++filt _Z5helloPKc
C++ が関数のオーバーロードをサポートしているのに C がサポートしていない理由も簡単に理解できます。これは、C++ では関数をシンボルに変更するときに関数のパラメーターの型を追加しますが、C では追加しないためです。関数名が同じであれば、パラメーターが異なっていれば、シンボル名は競合しません。次の例を通じて、変数名とシンボルの間のこの関係を確認できます。
/ * filename : test.cpp */
#include <stdio.h>
namespace myname
{
int var = 42;
}
extern int _ZN6myname3varE;
int main()
{
printf("%d\n", _ZN6myname3varE);
return 0;
}
ログイン後にコピー
ここでは、名前空間 namespace でグローバル変数 var を定義します。前の内容に従って、シンボル _ZN6myname3varE として変更され、外部変数 _ZN6myname3varE を手動で宣言して出力します。コンパイルして実行すると、その値はまさに var
$ gcc test.cpp -o test -lstdc++
$ ./test
42
ログイン後にコピー
extern "C" の値です
シンボルの概念を使えば、extern "C" の使い方が簡単に分かります
extern "C"
{
int func(int);
int var;
}
ログイン後にコピー
ということをコンパイラに伝えることを意味しますextern "C" の後の括弧内のコードは C コードとして扱われます。もちろん、単一のステートメント
extern "C" int func(int);
extern "C" int var;
ログイン後にコピー
で宣言して、C 型の func と var を宣言することもできます。多くの場合、ヘッダー ファイルを作成して C 言語関数を宣言しますが、これらの関数は C および C++ コードから呼び出すことができます。C++ コードから呼び出す場合は、ヘッダー ファイルに extern "C" を追加する必要があります。そうでない場合は、C++ を使用します。 C はそのような構文をサポートしていないため、シンボルが見つかりません。また、C コードを呼び出すときに extern "C" を追加することはできません。C ライブラリ関数 memset を例に説明します。このうち、__cplusplus は C++ コンパイラで定義されたマクロです。このコードを C++ でコンパイルすると、memset は extern "C" で宣言されます。C コードでコンパイルすると、__cplusplus が定義されていないため、直接宣言されます。 . なので、構文エラーは発生しません。この手法はシステム ヘッダー ファイルでよく使用されます。
C++ での extern "C" の使用方法の詳細な説明と関連記事については、PHP 中国語 Web サイトに注目してください。