C ++でのextern "C"の効果は何ですか?


1632

extern "C"C ++コードに入れると、正確にはどうなりますか?

例えば:

extern "C" {
   void foo();
}

82
私はこの記事を紹介したいと思います:http : //www.agner.org/optimize/calling_conventions.pdfこれは、呼び出し規約とコンパイラーの違いについてより多くを教えてくれます。
Sam Liao

1
@Litherum私の頭の上では、クロスコンパイラーがあるとすれば、Cを使用してその範囲のコードをコンパイルするようコンパイラーに指示しています。また、そのfoo()機能を備えたCppファイルがあることも意味します。
ha9u63ar 2013年

1
@ ha9u63ar「頭の上から」です。コメントの残り全体も正しくありません。削除することをお勧めします。
TamaMcGlinn

回答:


1559

extern "C"は、C ++の関数名に 'C'リンケージを持たせる(コンパイラは名前をマングルしない)ため、クライアントCコードは、関数の宣言。関数定義はバイナリ形式(C ++コンパイラーによってコンパイルされたもの)に含まれており、クライアントの「C」リンカーが「C」名を使用してリンクします。

C ++には関数名のオーバーロードがあり、Cにはないので、C ++コンパイラーは関数名をリンク先の一意のIDとして使用できないので、引数に関する情報を追加して名前をマングルします。Cで関数名をオーバーロードできないため、Cコンパイラーは名前をマングルする必要はありません。C++で関数にextern "C"リンケージがあると宣言した場合、C ++コンパイラーは、使用される名前に引数/パラメーター型情報を追加しません。リンケージ。

ご存知のように、個々の宣言/定義への "C"リンケージを明示的に指定するか、ブロックを使用して宣言/定義のシーケンスをグループ化し、特定のリンケージを持たせることができます。

extern "C" void foo(int);
extern "C"
{
   void g(char);
   int i;
}

専門性に関心がある場合は、C ++ 03標準のセクション7.5にリストされています。ここに簡単な要約を示します(extern "C"に重点を置いています)。

  • extern "C"はリンケージ仕様です
  • すべてのコンパイラは「C」リンケージを提供する必要があります
  • リンケージ仕様は名前空間スコープでのみ発生します
  • すべての関数タイプ、関数名、変数名には言語リンケージがあります リチャードのコメントを参照してください:言語リンケージがあるのは、外部リンケージがある関数名と変数名のみです
  • 言語リンケージが異なる2つの関数型は、他の点では同一であっても異なる型です
  • リンケージ仕様のネスト、内部の仕様は最終的なリンケージを決定します
  • extern "C"はクラスメンバーでは無視されます
  • 特定の名前を持つ多くても1つの関数は、「名前空間に関係なく」「C」リンケージを持つことができます
  • extern "C"は、関数に外部リンケージを強制します(静的にすることはできません) 。Richardのコメントを参照してください 。'extern "C" '内の' static 'は有効です。そのように宣言されたエンティティには内部リンケージがあり、したがって言語リンケージはありません
  • C ++から他の言語で定義されたオブジェクトへのリンク、および他の言語からC ++で定義されたオブジェクトへのリンクは、実装定義および言語依存です。2つの言語実装のオブジェクトレイアウト戦略が十分に類似している場合のみ、このようなリンケージを実現できます。

22
Cコンパイラは、c ++のようにマングリングを使用しません。したがって、c ++プログラムからacインターフェイスを呼び出す場合は、cインターフェイスが「extern c」であることを明確に宣言する必要があります。
Sam Liao

59
@ファイサル:相互参照がすべて「extern "C"」であっても、異なるC ++コンパイラでビルドされたコードをリンクしようとしないでください。多くの場合、クラスのレイアウト、例外の処理に使用されるメカニズム、または変数が使用前に初期化されることを保証するために使用されるメカニズム、またはその他のそのような相違点に違いがあります。さらに、2つの別個のC ++ランタイムサポートライブラリ(1つは各コンパイラ)。
ジョナサンレフラー

8
「extern "C"は関数に外部リンケージを強制します(静的にすることはできません)」は正しくありません。'extern "C"'内の 'static'は有効です。そのように宣言されたエンティティには内部リンケージがあり、したがって言語リンケージはありません。
リチャードスミス

14
「すべての関数型、関数名、変数名には言語リンケージがある」も正しくありません。言語リンケージがあるのは、外部リンケージのある関数名と変数名だけです。
Richard Smith

9
これextern "C" { int i; }は定義です。これは、の非定義の隣で、意図したものではない可能性がありますvoid g(char);。これを非定義にするには、が必要になりますextern "C" { extern int i; }。一方、中括弧のない1つの宣言の構文は、宣言を非定義にします。これextern "C" int i;extern "C" { extern int i; }
aschepler

327

まだ投稿されていないので、ちょっと情報を追加したかっただけです。

Cヘッダーに次のようなコードが表示されることがよくあります。

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

これにより、マクロ "__cplusplus"が定義されるため、CヘッダーファイルをC ++コードで使用できるようになります。しかし、あなたはできるまだマクロがされたレガシーCコードでそれを使用しないで、それが一意にC ++構文は表示されませんので、定義されました。

ただし、次のようなC ++コードも見ました。

extern "C" {
#include "legacy_C_header.h"
}

私が想像するのとほぼ同じことを達成します。

どちらの方法が良いかわかりませんが、両方を見ました。


11
明確な違いがあります。前者の場合、このファイルを通常のgccコンパイラーでコンパイルすると、関数名がマングルされていないオブジェクトが生成されます。その後、CおよびC ++オブジェクトをリンカーにリンクすると、関数が見つかりません。2番目のコードブロックのように、これらの「レガシーヘッダー」ファイルにexternキーワードを含める必要があります。
アンヴァンロッサム2013

8
@Anne:C ++コンパイラーはextern "C"、ヘッダーにあるため、符号化されていない名前も検索します。このテクニックを何度も使用して、うまくいきました。
Ben Voigt 2014年

20
@アン:そうではありません。最初のものも大丈夫です。Cコンパイラでは無視され、C ++の2番目と同じ効果があります。コンパイラーextern "C"は、ヘッダーが含まれる前または後に遭遇するかどうかを気にすることができませんでした。コンパイラに到達するまでには、いずれにしても、前処理されたテキストの1つの長いストリームにすぎません。
Ben Voigt 2014年

8
@Anne、いいえ、あなたが説明していることが間違っているため、ソースの他のエラーの影響を受けたと思います。どのバージョンg++でも、少なくとも過去17年間のどの時点においても、これは間違っていません。最初の例の要点は、CコンパイラとC ++コンパイラのどちらを使用するかは問題ではなく、extern "C"ブロック内の名前に対して名前の変換は行われないということです。
Jonathan Wakely 2016年

7
「どちらが良い」-確かに、最初のバリアントはより優れています:CおよびC ++コードの両方で、それ以上の要件なしで、直接ヘッダーを含めることができます。2番目のアプローチは、C ++ガードを忘れたCヘッダーの回避策です(ただし、これらが後で追加された場合、ネストされたextern "C"宣言が受け入れられます...)。
アコンカグア2017

267

g++生成されたバイナリを逆コンパイルして、何が起こっているかを確認します

main.cpp

void f() {}
void g();

extern "C" {
    void ef() {}
    void eg();
}

/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }

生成されたELF出力をコンパイルして逆アセンブルします。

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

出力には以下が含まれます。

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

解釈

私たちはそれを見る:

  • efそしてeg、コードと同じ名前のシンボルに保存されました

  • 他のシンボルは壊されました。それらを分解してみましょう:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()

結論:次のシンボルタイプは両方ともマングルされませんでした

  • 定義された
  • 宣言されているが未定義(Ndx = UND)、リンク時または実行時に別のオブジェクトファイルから提供される

したがってextern "C"、呼び出すときに両方が必要になります。

  • C ++からのC:g++によって生成された符号化されていないシンボルを期待するように伝えるgcc
  • C ++からC:使用するg++ためgccに符号化されていないシンボルを生成するように指示する

extern Cで機能しないもの

名前のマングリングを必要とするC ++機能は、内部では機能しないことが明らかになりますextern C

extern "C" {
    // Overloading.
    // error: declaration of C function ‘void f(int)’ conflicts with
    void f();
    void f(int i);

    // Templates.
    // error: template with C linkage
    template <class C> void f(C i) { }
}

C ++の例からの最小限の実行可能なC

完全性とそこに登場するnewbsについては、「C ++プロジェクトでCソースファイルを使用する方法」も参照してください

C ++からCを呼び出すのは非常に簡単です。各C関数には、マングルされていない可能性のあるシンボルが1つしかないため、追加の作業は必要ありません。

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

ch

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

cc

#include "c.h"

int f(void) { return 1; }

実行:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

せずにextern "C"リンクして失敗します。

main.cpp:6: undefined reference to `f()'

生成されなかっg++たマングルを見つけることを期待しているためです。fgcc

GitHubの例

Cの例からの最小限の実行可能なC ++

CからC ++を呼び出すのは少し難しいです。公開する各関数のマングルされていないバージョンを手動で作成する必要があります。

ここでは、C ++関数のオーバーロードをCに公開する方法を示します。

main.c

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

実行:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

extern "C"それなしでは失敗します:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

なぜならg++、生成マングルされたシンボルgccを見つけることができません。

GitHubの例

Ubuntu 18.04でテスト済み。


21
ベストの答えあなたは、1)明示的に言及しているのでextern "C" {、あなたが呼び出すことができますC ++プログラム内からunmangled C関数をだけでなく、Cプログラム内からの関数++ unmangled C他の回答はそれほど明らかにしない、あなたがの明確な例を示しているため)2各。ありがとう!
ガブリエルステープルズ2018年

3
私はこの答えがとても好きです
セルフブート

4
Cからオーバーロードされた関数を呼び出す方法が示されているので、ベストアンサーを伝えます
Gaspa79

1
@JaveneCPPMcGowan私にC ++の先生がいたとどう思われますか :-)
Ciro Santilli冠状病毒审查六四事件法轮功

205

すべてのC ++プログラムでは、すべての非静的関数はバイナリファイルでシンボルとして表されます。これらの記号は、プログラム内の関数を一意に識別する特別なテキスト文字列です。

Cでは、シンボル名は関数名と同じです。これが可能なのは、Cでは2つの非静的関数が同じ名前を持つことができないためです。

C ++はオーバーロードを許可し、Cにはない多くの機能(クラス、メンバー関数、例外仕様など)を備えているため、単純に関数名をシンボル名として使用することはできません。これを解決するために、C ++は、関数名とすべての必要な情報(引数の数とサイズなど)を、コンパイラーとリンカーによってのみ処理される奇妙に見える文字列に変換する、いわゆる名前マングリングを使用します。

そのため、関数をextern Cとして指定すると、コンパイラーはそれを使用して名前のマングルを実行せず、シンボル名を関数名として使用して直接アクセスできます。

これは、そのような関数の使用中dlsym()dlopen()呼び出し時に便利です。


便利とはどういう意味ですか?シンボル名=関数名は、dlsymに渡されるシンボル名を既知にするか、または他のことですか?
エラー

1
@エラー:はい。一般的なケースでは、ヘッダーファイルのみを指定してC ++共有ライブラリをdlopen()し、ロードする適切な関数を選択することは基本的に不可能です。(x86では、C ++関数名を壊すために私が知っているすべてのx86コンパイラーがItanium ABIの形式で公開された名前変換仕様がありますが、言語では何もこれを必要としません。)
Jonathan Tomer

52

C ++は関数名をマングルして、手続き型言語からオブジェクト指向言語を作成します

ほとんどのプログラミング言語は、既存のプログラミング言語の上に構築されていません。C ++はCの上に構築されており、さらに手続き型プログラミング言語から構築されたオブジェクト指向プログラミング言語です。そのため、C extern "C"との下位互換性を提供するようなC ++式があります。

次の例を見てみましょう:

#include <stdio.h>

// Two functions are defined with the same name
// but have different parameters

void printMe(int a) {
  printf("int: %i\n", a);
}

void printMe(char a) {
  printf("char: %c\n", a);
}

int main() {
  printMe("a");
  printMe(1);
  return 0;
}

同じ関数printMeが2回定義されているため、Cコンパイラーは上記の例をコンパイルしません(たとえパラメーターint avs が異なっていてもchar a)。

gcc -o printMe printMe.c && ./printMe;
1つのエラー。PrintMeが複数回定義されています。

C ++コンパイラは上記の例をコンパイルします。printMe二度定義されても構いません。

g ++ -o printMe printMe.c && ./printMe;

これは、C ++コンパイラーがパラメーターに基づいて暗黙的に関数の名前を変更する(mangles)ためです。Cでは、この機能はサポートされていませんでした。ただし、C ++がC上に構築された場合、言語はオブジェクト指向になるように設計され、同じ名前のメソッド(関数)で異なるクラスを作成し、異なるクラスに基づいてメソッド(メソッドオーバーライド)をオーバーライドする機能をサポートする必要がありました。パラメーター。

extern "C" 「Cの関数名を壊さないでください」と言う

ただし、「parent.c」というinclude名前のレガシーCファイルがあり、他のレガシーCファイルからの関数名である「parent.h」、「child.h」などがあるとします。レガシー「parent.c」ファイルが実行されている場合C ++コンパイラを使用すると、関数名はマングルされ、「parent.h」、「child.h」などで指定された関数名と一致しなくなります。したがって、これらの外部ファイルの関数名も必要になります。壊される。複雑なCプログラム(依存関係が多いプログラム)全体で関数名をマングルすると、コードが壊れる可能性があります。したがって、C ++コンパイラに関数名を壊さないように指示できるキーワードを提供すると便利な場合があります。

extern "C"キーワードはありませんマングル(名前変更)Cの関数名にC ++コンパイラに指示します。

例えば:

extern "C" void printMe(int a);


我々は、使用していないことができextern "C"、我々はちょうど持っている場合はdll、ファイルを?つまり、ヘッダーファイルがなく、ソースファイル(実装のみ)があり、関数ポインターを介してその関数を使用している場合です。この状態では、(名前に関係なく)関数を使用しました。
BattleTested 2018年

@tfmontague、私のためにあなたはそれを正しく釘付けしました!頭の中でまっすぐ。
Artanis Zeratul

29

extern "C"でラップするだけでは、CヘッダーをC ++と互換にすることはできません。Cヘッダー内の識別子がC ++キーワードと競合する場合、C ++コンパイラはこれについて文句を言います。

たとえば、次のコードがg ++で失敗するのを見ました。

extern "C" {
struct method {
    int virtual;
};
}

ちょっと意味はありますが、CコードをC ++に移植するときに覚えておくべきことです。


14
extern "C"他の回答で説明されているように、Cリンケージを使用することを意味します。「内容をCとしてコンパイルする」という意味ではありません。int virtual;C ++では無効であり、別のリンケージを指定してもそれは変わりません。
MM

1
...またはモード一般に、構文エラーを含むコードはコンパイルされません。
Valentin Heinitz、2017年

4
@ValentinHeinitzは当然ですが、Cで識別子として「仮想」を使用することは構文エラーではありません。私はちょうどあなたが自動的に使用できないという点まで欲しかった任意のその周りにextern「C」を置くことによって、C ++でCヘッダーを。
サンダーメルテンス2017年

28

これは、関数がCから呼び出せるように関数のリンケージを変更します。実際には、関数名はマングルされません。


3
マングルとは、一般的に使用される用語です...この意味で「装飾された」が使用されるのを見たことがあるとは思わないでください。
Matthew Scharley、2009年

1
Microsoftは(少なくとも部分的に)文書を壊すのではなく装飾を使用しています。彼らは自分のツールに名前を付けて装飾を解除する(別名アンマングルする)ことさえしていますundname
ルネNyffenegger

20

リンク時にCおよびC ++でコンパイルされた関数の名前が異なるため、リンク時にCスタイルでこれらの関数の名前を検索するようにC ++コンパイラーに通知します。


12

extern "C"は、C ++コンパイラによって認識され、指定された関数がCスタイルでコンパイルされている(またはコンパイルされる)ことをコンパイラに通知するためのものです。そのため、リンク中に、Cからの正しいバージョンの関数にリンクします。


6

以前はdll(ダイナミックリンクライブラリ)ファイルなどに「extern "C"」を使用して、main()関数を「exportable」にしていたため、後でdllから別の実行可能ファイルで使用できます。多分私がそれを使用していた場所の例が役に立つかもしれません。

DLL

#include <string.h>
#include <windows.h>

using namespace std;

#define DLL extern "C" __declspec(dllexport)
//I defined DLL for dllexport function
DLL main ()
{
    MessageBox(NULL,"Hi from DLL","DLL",MB_OK);
}

EXE

#include <string.h>
#include <windows.h>

using namespace std;

typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder

int main()
{
    char winDir[MAX_PATH];//will hold path of above dll
    GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
    strcat(winDir,"\\exmple.dll");//concentrate dll name with path
    HINSTANCE DLL = LoadLibrary(winDir);//load example dll
    if(DLL==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if load fails exit
        return 0;
    }
    mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main");
    //defined variable is used to assign a function from dll
    //GetProcAddress is used to locate function with pre defined extern name "DLL"
    //and matcing function name
    if(mainDLLFunc==NULL)
    {
        FreeLibrary((HMODULE)DLL);//if it fails exit
        return 0;
    }
    mainDLLFunc();//run exported function 
    FreeLibrary((HMODULE)DLL);
}

4
偽物。extern "C"__declspec(dllexport)は無関係です。前者はシンボルの装飾を制御し、後者はエクスポートエントリの作成を担当します。C ++の名前の装飾を使用してシンボルをエクスポートすることもできます。この質問の要点を完全に逃しているだけでなく、コードサンプルにも他の誤りがあります。1つは、mainDLLからエクスポートしても戻り値が宣言されないことです。またはそのことについては、呼び出し規約。インポートするときは、ランダムな呼び出し規約(WINAPI)を指定し、32ビットビルド(_mainまたは_main@0)に間違ったシンボルを使用します。すみません、-1。
IInspectable 2016

1
それは繰り返されただけで、あなたが知らないこと、あなたが何をしているのか、しかしこの方法でそれを行うことは、いくつかの非公開のターゲットプラットフォームのリストではうまくいくようです。以前のコメントで私が提起した問題に対処しなかった。非常に間違っているため、これはまだ反対票です(さらに、1つのコメントに収まらなかった)。
IInspectable 2017年

1
Stack Overflowに回答を投稿することは、何をしているのかを知っていることを意味します。これは予想通りです。「実行時にスタックの破損を防止する」試み:関数のシグネチャはtypeの戻り値を指定してvoid*いますが、実装は何も返しません。それは本当にうまく飛ぶでしょう...
IInspectable '25年

1
あなたは何か、それを実装した場合表示され、純粋な運の作品にし、その後、あなたがはっきりんではないあなたは何をしているかを知っている(あなたの「作業」のサンプルは、そのカテゴリに該当します)。これは未定義の動作であり、動作しているように見えることは未定義の動作の有効な形式です。まだ定義されていません。今後もさらに頑張っていただければ幸いです。その一部は、この提案された回答の削除である可能性があります。
IInspectable 2017年

1
何も返さない関数を、ポインターを返す関数として再解釈しています。x86が関数のシグネチャの不一致、特に整数型の戻り値に関して非常に寛容であることは、幸運です。コードは偶然にのみ機能します。同意しない場合は、コードが確実に機能する理由を説明する必要があります。
IInspectable 2017年

5

extern "C"CppソースファイルC関数呼び出すために使用されるリンケージ仕様ですC関数呼び出し、変数を記述し、ヘッダーを含めることができます。関数は外部エンティティで宣言されており、外部で定義されています。構文は

タイプ1:

extern "language" function-prototype

タイプ2:

extern "language"
{
     function-prototype
};

例えば:

#include<iostream>
using namespace std;

extern "C"
{
     #include<stdio.h>    // Include C Header
     int n;               // Declare a Variable
     void func(int,int);  // Declare a function (function prototype)
}

int main()
{
    func(int a, int b);   // Calling function . . .
    return 0;
}

// Function definition . . .
void func(int m, int n)
{
    //
    //
}

3

この回答は、せっかちな人/締め切りに間に合わせるためのもので、一部/簡単な説明は以下のとおりです。

  • C ++では、オーバーロードを介してクラスに同じ名前を付けることができます(たとえば、すべて同じ名前はそのままdllからエクスポートできないためなど)これらの問題の解決策は、それらが異なる文字列(シンボルと呼ばれる)に変換されることです)、シンボルは関数の名前と引数を説明するため、同じ名前でもこれらの各関数を一意に識別できます(名前マングリングとも呼ばれます)。
  • Cでは、オーバーロードはありません。関数名は一意です(したがって、関数名を一意に識別するための個別の文字列は必要ないため、シンボルは関数名そのものです)

したがって
、C ++では、名前をマングル化
してCの各関数を一意に識別します。名前をマングル化して各関数を一意に識別しなくても

C ++の動作を変更するには、つまり、特定の関数で名前のマングリング発生しないように指定するには、dllから特定の名前の関数をエクスポートするなど、何らかの理由で関数名の前にextern "C"を使用できます。、そのクライアントが使用します。

より詳細な/より正しい答えについては、他の答えを読んでください。


1

CとC ++を混在させる場合(つまり、a。C ++からのC関数の呼び出し、およびb。CからのC ++関数の呼び出し)、C ++名のマングリングによりリンクの問題が発生します。技術的に言えば、この問題は、呼び出し先の関数が対応するコンパイラを使用してすでにバイナリ(おそらく* .aライブラリファイル)にコンパイルされている場合にのみ発生します。

したがって、C ++で名前のマングリングを無効にするには、extern "C"を使用する必要があります。


0

他の良い答えと競合することなく、少し例を追加します。

C ++コンパイラが正確に行うこと:コンパイルプロセスで名前を壊すため、コンパイラに実装を特別に処理する ように指示する必要がありCます。

C ++クラスを作成してを追加するときextern "C"、C呼び出し規約を使用していることをC ++コンパイラーに伝えます。

理由(C ++からC実装を呼び出す): C ++からC関数を呼び出すか、CからC ++関数を呼び出す(C ++クラスなどはCでは機能しません)。


Stack Overflowへようこそ。十分に確立された正しい回答のある古い質問に回答することにした場合、その日の後半に新しい回答を追加しても、クレジットを得られない可能性があります。特徴的な新しい情報がある場合、または他の回答がすべて間違っていると確信している場合は、必ず新しい回答を追加してください。あなたに多くの信用を与えます。正直なところ、私はこの答えに何か新しいことはないと思います。
ジョナサンレフラー

さて、私はあなたの要点を覚えている必要があります
わかりました

-1

Cコンパイラーによってコンパイルされた関数void f()と、C ++コンパイラーによってコンパイルされた同じ名前void f()を持つ関数は、同じ関数ではありません。その関数をCで記述し、C ++から呼び出そうとした場合、リンカーはC ++関数を検索し、C関数を見つけません。

extern "C"は、Cコンパイラによってコンパイルされた関数があることをC ++コンパイラに伝えます。Cコンパイラによってコンパイルされたことを伝えると、C ++コンパイラはそれを正しく呼び出す方法を認識します。

また、C ++コンパイラがC ++関数を呼び出すことができるようにC ++関数をコンパイルすることもできます。その関数は正式にはC関数ですが、C ++コンパイラーによってコンパイルされるため、すべてのC ++機能を使用でき、すべてのC ++キーワードを持っています。


C ++コンパイラーはextern "C"関数をコンパイルできます—(いくつかの制約を条件として)Cコンパイラーによってコンパイルされたコードから呼び出すことができます。
Jonathan Leffler
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.