1933

C ++コードに入れることは正確に何をextern "C"しますか?

例えば:

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

16 に答える 16

1801

extern "C"C++ の関数名に C リンケージを持たせ (コンパイラは名前をマングルしません)、クライアントの C コードが、関数の宣言だけを含む C 互換ヘッダー ファイルを使用して関数にリンク (使用) できるようにします。関数定義は、クライアント C リンカーが C 名を使用してリンクするバイナリ形式 (C++ コンパイラによってコンパイルされたもの) に含まれています。

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

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

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

専門的なことが気になる場合は、C++03 標準のセクション 7.5 に記載されています。ここに簡単な要約を示します (強調部分はextern "C")。

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

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

次のような 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"
}

ほぼ同じことを達成していると思います。

どちらが良いかはわかりませんが、私は両方を見てきました。

于 2012-10-21T01:08:32.503 に答える
216

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

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

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

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

dlsym()これは、そのような関数を使用したりdlopen()呼び出したりするときに便利です。

于 2009-06-25T05:22:05.773 に答える
73

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;
}

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

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

C++ コンパイラは上記の例をコンパイルします。printMe2 回定義されてもかまいません。

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);

于 2017-02-12T01:50:27.233 に答える
32

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

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

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

ちょっと理にかなっていますが、C コードを C++ に移植するときに留意すべき点です。

于 2013-01-09T22:16:05.167 に答える
30

関数がCから呼び出せるように、関数のリンクを変更します。実際には、関数名がマングルされていないことを意味します。

于 2009-06-25T02:12:21.717 に答える
21

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

于 2009-06-25T02:12:12.870 に答える
12

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

于 2012-04-10T09:46:10.060 に答える
7

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

タイプ 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)
{
    //
    //
}
于 2015-11-17T12:09:03.657 に答える