2008-09-15 15 views
117

は、なぜ私たちが使用する必要があります。具体的にC#でextern "C" {#include <foo.h>}が必要なのはなぜですか?

extern "C" { 
#include <foo.h> 
} 

  • は、我々はそれを使用する必要がありますか?

  • コンパイラ/リンカレベルで何が起こっているのですか?

  • これは、コンパイル/リンクの面でどのように私たちがそれを使用する必要がある問題を解決しますか?

+0

私はあなたの質問のタイトルで何を意味するのか混乱しています...あなたは精緻化できますか? –

+29

私はそれをどのように置くべきか分かりません。タイトルを超えて読んだことはありますか? – Landon

答えて

106

CとC++は表面的には似ていますが、それぞれは非常に異なるコードセットにコンパイルされます。 C++コンパイラでヘッダファイルをインクルードすると、コンパイラはC++コードを期待しています。ただし、Cヘッダーの場合、コンパイラーはヘッダーファイルに含まれているデータをC++ 'ABI'または 'Application Binary Interface'のような特定の形式にコンパイルする必要があります。これは、C++データをCデータが必要な関数に渡すよりも好ましい方法です。

(そのC関数としてプロトタイプにフラグを立てることなく、printf()を呼び出して、本当に核心ザラザラ、C++のABI一般に 『マングル』その関数/メソッドの名前に取得するには、C++が実際に_Zprintfを呼び出すコードを生成します、プラス最後に余計なことがあります。)

したがって、ACヘッダを含めるとextern "C" {...};を使用してください。これは簡単です。そうしないと、コンパイルされたコードに不一致があり、リンカーがチョークします。しかしほとんどのヘッダでは、externは必要ありません。ほとんどのシステムCヘッダは、すでにC++コードに組み込まれており、コードがすでにexternである可能性があるためです。

+1

もっと詳しく教えてください。** "ほとんどのシステムCヘッダーは、既にC++コードに含まれていて、コードにすでに含まれているかもしれないという事実を説明しています。 –

+3

@BulatM。 '#ifdef __cplusplus extern" C "{ #endif' C++ファイルからインクルードすると、Cヘッダとして扱われます。 – Calmarius

+1

いいえ ';'最後に必要です。私は変更を適用するために少なくとも6つの文字を変更する必要があるので、変更できません - 愚かな – danger89

13

これは、異なるコンパイラが名前マングリングを実行する方法と関係があります。 C++コンパイラは、Cコンパイラとはまったく異なる方法でヘッダファイルからエクスポートされたシンボルの名前を変更するので、リンクしようとするとシンボルが見つからないというリンカエラーが発生します。

これを解決するために、C++コンパイラに "C"モードで実行するよう指示します。そのため、Cコンパイラと同じ方法で名前のマングリングを実行します。これを実行すると、リンカのエラーは修正されます。

5

名前のマングリングの問題を解決するために使用します。 extern Cは、関数が「フラット」なCスタイルのAPIであることを意味します。

6

C++コンパイラは、Cコンパイラとは異なるシンボル名を作成します。したがって、CコードとしてコンパイルされたCファイルにある関数を呼び出そうとしている場合、C++コンパイラに解決しようとしているシンボル名がデフォルト以外のものであることを通知する必要があります。それ以外の場合、リンクステップは失敗します。

18

C++では、名前を共有する異なるエンティティを持つことができます。

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

それらすべてを区別するために、C++コンパイラ:たとえば、ここでの機能のリストは、すべての名前付きfooというですネームマングリングやデコレーションと呼ばれるプロセス内でそれぞれ固有の名前を作成します。 Cコンパイラはこれをしません。さらに、それぞれのC++コンパイラはこれを行う方法が異なります。

にextern「C」は、中括弧内のコードに任意の名前マングリングを行わないC++コンパイラに指示します。これにより、C++からC関数を呼び出すことができます。

10

CとC++は記号の名前について異なる規則を持っています。シンボルは、コンパイラによって生成された1つのオブジェクトファイルの関数 "openBankAccount"への呼び出しが、同じ(互換性のある)別のオブジェクトファイルから生成された別のオブジェクトファイルで "openBankAccount"コンパイラ。これにより、複数のソースファイルからプログラムを作成することができます。これは、大規模なプロジェクトで作業する際の安心です。

Cでは、ルールは非常に単純で、シンボルはすべて単一の名前空間にあります。したがって、整数 "socks"は "socks"として格納され、関数count_socksは "count_socks"として格納されます。

リンカーは、この単純なシンボル命名規則でCやCのような他の言語用にビルドされました。リンカのシンボルは単純な文字列です。

しかし、C++に言語を使用すると、名前空間、および多型と、このような単純なルールを持つ様々な他の事その葛藤を持っていることができます。 "add"と呼ばれる6つのポリモーフィック関数はすべて、異なるシンボルを持つ必要があります。間違ったものは他のオブジェクトファイルで使用されます。これは、記号の名前を「mangling」(それは専門用語である)することによって行われます。

C++コードをCライブラリまたはコードにリンクする場合、Cライブラリのヘッダーファイルなど、C言語で書かれたものはすべてextern "C"にする必要があります。これらのシンボル名は、あなたのC++コードの残りの部分は混乱しているか、動作しません。

10

我々はそれを使用する必要がありますか?

あなたはそれを使用するために私たちに を必要 コンパイラ/リンカレベルで何が起こっているファイル

をC++オブジェクトへのCするライブラリをリンクしている場合は?

CおよびC++は、シンボルの命名のための別のスキームを使用します。これは、与えられたライブラリにリンクするときにCのスキームを使用するようにリンカに指示します。

どのようにコンパイルの面で/これは、それを使用することを求め 問題を解決しない を結びますか?

Cネーミングスキームを使用すると、Cスタイルのシンボルを参照できます。それ以外の場合、リンカは動作しないC++スタイルのシンボルを試します。

5

構文は、中括弧内で宣言された名前に対してマングリングを実行しないようにコンパイラに指示します。通常、C++コンパイラは関数名を「拡張」し、引数と戻り値に関する型情報をエンコードします。これは、と呼ばれ、名前がとなっています。 extern "C"構成は、マングリングを防止する。

通常、C++コードでC言語ライブラリを呼び出す必要がある場合に使用されます。また、C++関数(例えば、DLLから)をCクライアントに公開するときにも使用できます。

104

extern "C"は、生成されたオブジェクトファイル内のシンボルの名前付け方法を決定します。関数がextern "C"なしで宣言されている場合、オブジェクトファイルのシンボル名はC++ネームマングリングを使用します。ここに例があります。そのような

考えるtest.cの:

void foo() { } 

コンパイルし、オブジェクトファイル内のシンボルをリストが得られます。

$ g++ -c test.C 
$ nm test.o 
0000000000000000 T _Z3foov 
       U __gxx_personality_v0 

のfoo関数は、実際に "_Z3foov" と呼ばれています。この文字列には、戻り値の型とパラメータなどの型情報が含まれます。その後

extern "C" { 
    void foo() { } 
} 

コンパイルし、シンボルを見て:あなたの代わりにこのようTEST.Cを書く場合

$ g++ -c test.C 
$ nm test.o 
       U __gxx_personality_v0 
0000000000000000 T foo 

あなたはCリンケージを取得します。オブジェクトファイルの "foo"関数の名前はちょうど "foo"であり、名前のマングリングに由来するすべての派手な型情報を持っていません。

一般的に、extern "C" {}内には、それと共に使用するコードがCコンパイラでコンパイルされていても、C++から呼び出そうとしている場合にヘッダが含まれています。これを行うと、ヘッダー内のすべての宣言がCリンケージを使用することをコンパイラーに伝えます。あなたのコードをリンクすると、あなたの.oファイルには "_Z3fooblah"ではなく、 "foo"への参照が含まれます。これはあなたがリンクしているライブラリにあるものとうまく一致します。

ほとんどの現代の図書館は、そのようなヘッダーの周りにガードを置いて、シンボルが正しいリンケージで宣言されるようにします。例えばあなたが見つける標準ヘッダーの多くに:

#ifdef __cplusplus 
extern "C" { 
#endif 

... declarations ... 

#ifdef __cplusplus 
} 
#endif 

これは、C++のコードはヘッダが含まれている場合、あなたのオブジェクトファイル内のシンボルは、Cライブラリには何が一致することを確認します。 Cヘッダにextern "C" {}を置く必要があるのは、古いもので、すでにこれらのガードを持っていない場合だけです。

+2

いくつかの簡単な例は本当に良いことです! – zhy

6

extern "C"は、Cコンパイラでコンパイルされたファイルにあるヘッダ定義関数をインクルードするときに使用する必要があります。 (多くの標準Cライブラリでは、ヘッダーにこのチェックが含まれているため、開発者にとっては簡単になります)

たとえば、3つのファイル、util.c、util.h、およびmain.cppのプロジェクトがある場合.cファイルと.cppファイルはC++コンパイラ(g ++、ccなど)でコンパイルされているので、実際には必要ではなく、リンカエラーが発生することさえあります。あなたのビルドプロセスがutil.cのための通常のCコンパイラを使用するならば、util.hを含めるときはextern "C"を使う必要があります。

何が起こっているかは、C++が関数のパラメータをその名前でエンコードすることです。これが関数のオーバーロードの仕組みです。C関数に起こりがちなのは、名前の先頭にアンダースコア(_)を追加することだけです。 extern "C"を使用せずに、関数の実際の名前が_DoSomething()またはDoSomething()の場合、リンカはDoSomething @@ int @ float()という名前の関数を探します。

extern "C"を使用すると、C++コンパイラに、C++の代わりにCの命名規則に従う関数を探すべきであることを伝えることで、上記の問題を解決します。

関連する問題