C++ での extern 'C' の使用法の詳細な説明

高洛峰
リリース: 2016-12-19 14:47:48
オリジナル
2005 人が閲覧しました

簡単に言うと、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 サイトに注目してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート