365

レガシーCコードがたくさんあるプロジェクトに取り組んでいます。最終的にレガシーコードも変換することを目的として、C++での記述を開始しました。CとC++がどのように相互作用するかについて少し混乱しています。Cコードをextern "C"C++コンパイラでラップしても、 Cコードの名前が壊れないことは理解していますが、これを実装する方法が完全にはわかりません。

したがって、各Cヘッダーファイルの上部(インクルードガードの後)には、次のようになります。

#ifdef __cplusplus
extern "C" {
#endif

一番下に、

#ifdef __cplusplus
}
#endif

2つの間に、インクルード、typedef、関数プロトタイプがすべてあります。これを正しく理解しているかどうかを確認するために、いくつか質問があります。

  1. CヘッダーファイルBhを含み、別のCヘッダーファイルChを含むC ++ファイルA.hhがある場合、これはどのように機能しますか?コンパイラがBhにステップインすると、が定義されると思います。その __cplusplusため、コードはでラップされますextern "C"__cplusplusこのブロック内では定義されません)。したがって、Chにステップインするとき、 __cplusplusは定義されず、コードはでラップされません extern "C"。これは正しいです?

  2. コードの一部をでラップすることに何か問題があり extern "C" { extern "C" { .. } }ますか?2番目は何をextern "C" しますか?

  3. このラッパーを.cファイルの周りに配置するのではなく、.hファイルだけを配置します。では、関数にプロトタイプがない場合はどうなりますか?コンパイラはそれがC++関数だと思いますか?

  4. また、 Cで記述されたサードパーティのコードを使用しており、このようなラッパーはありません。そのライブラリのヘッダーを含めるときはいつでもextern "C"、#includeを回避しています。これはそれに対処する正しい方法ですか?

  5. 最後に、これは良いアイデアですか?他にやるべきことはありますか?近い将来、 CとC ++を混合する予定であり、すべてのベースをカバーしていることを確認したいと思います。

4

4 に答える 4

328

extern "C"コンパイラがコードを読み取る方法は実際には変更されません。コードが.cファイルにある場合はCとしてコンパイルされ、.cppファイルにある場合はC ++としてコンパイルされます(構成に奇妙なことをしない限り)。

extern "C"リンケージに影響を与えるのは何ですか。C ++関数は、コンパイルされると、名前が壊れます。これにより、オーバーロードが可能になります。関数名は、パラメーターのタイプと数に基づいて変更されるため、同じ名前の2つの関数のシンボル名は異なります。

内のコードextern "C"はまだC++コードです。extern "C"ブロックで実行できることには制限がありますが、それらはすべてリンケージに関するものです。Cリンケージで作成できない新しいシンボルを定義することはできません。たとえば、クラスやテンプレートがないことを意味します。

extern "C"ブロックはうまくネストします。extern "C++"絶望的に地域の中に閉じ込められていることに気付いた場合もありますextern "C"が、清潔さの観点からはあまり良い考えではありません。

さて、特にあなたの番号付きの質問に関して:

#1に関して:__cplusplusはextern "C"ブロック内で定義されたままになります。ただし、ブロックはきちんとネストする必要があるため、これは問題ではありません。

#2に関して:__ cplusplusは、C++コンパイラーを介して実行されているすべてのコンパイル単位に対して定義されます。一般に、これは.cppファイルとその.cppファイルに含まれるすべてのファイルを意味します。同じ.h(または.hhまたは.hppまたはwhat-have-you)は、異なるコンパイル単位に含まれている場合、異なる時点でCまたはC++として解釈される可能性があります。.hファイル内のプロトタイプがCシンボル名を参照するようにするextern "C"場合は、C ++として解釈されるときに持つ必要があり、Cextern "C"として解釈されるときに持つべきではありません。したがって、#ifdef __cplusplusチェックします。

質問3に答えるには、プロトタイプのない関数は、extern "C"ブロック内ではなく.cppファイル内にある場合、C++リンケージを持ちます。ただし、プロトタイプがない場合は、同じファイル内の他の関数からのみ呼び出すことができ、その関数を使用する予定がないため、リンケージがどのように見えるかは通常気にしないため、これは問題ありません。とにかく、同じコンパイルユニットの外部の何かによって呼び出されます。

#4の場合、あなたはそれを正確に持っています。Cリンケージを持つコード(Cコンパイラーによってコンパイルされたコードなど)のヘッダーを含める場合は、ヘッダーを作成する必要がありextern "C"ます。これにより、ライブラリーとリンクできるようになります。(そうでない場合、リンカーは、探してい_Z1hicたときのような名前の関数を探します。void h(int, char)

5:この種のミキシングは、使用する一般的な理由ですextern "C"。この方法でそれを行うことに何の問題もありません。自分が何をしているのかを理解していることを確認してください。

于 2010-09-24T17:32:50.127 に答える
51
  1. extern "C"__cplusplusマクロの有無は変更されません。ラップされた宣言のリンケージと名前マングリングを変更するだけです。

  2. extern "C"ブロックを非常に楽しくネストできます。

  3. .cファイルをC++としてコンパイルすると、extern "C"ブロック内になく、extern "C"プロトタイプがないものはすべてC++関数として扱われます。それらをCとしてコンパイルすると、もちろんすべてがC関数になります。

  4. はい

  5. このようにして、CとC++を安全に組み合わせることができます。

于 2010-09-24T17:08:01.300 に答える
25

Andrew Shelanskyの優れた答えの共同体であり、少し意見が合わないいくつかの落とし穴は、コンパイラがコードを読み取る方法を実際には変更しません。

関数プロトタイプはCとしてコンパイルされるため、同じ関数名を異なるパラメーターでオーバーロードすることはできません。これは、コンパイラーの名前マングリングの重要な機能の1つです。これはリンケージの問題として説明されていますが、それは完全には真実ではありません。コンパイラとリンカの両方からエラーが発生します。

コンパイラエラーは、オーバーロードなどのプロトタイプ宣言のC++機能を使用しようとした場合に発生します。

宣言の周りにextern"C"ラッパーがなく、ヘッダーがCとC ++のソースの混合に含まれている場合、関数が見つからないように見えるため、リンカーエラーが後で発生します。

コンパイルCをC++設定として使用することを思いとどまらせる理由の1つは、ソースコードの移植性がなくなったことを意味するためです。その設定はプロジェクト設定であるため、.cファイルが別のプロジェクトにドロップされた場合、c++としてコンパイルされません。むしろ、ファイルのサフィックスの名前を.cppに変更するのに時間をかけたいと思います。

于 2014-08-26T08:15:16.780 に答える
5

これは、CとC++の両方のアプリケーションが問題なくCインターフェイスを使用できるようにするためのABIに関するものです。

C言語は非常に簡単であるため、コード生成は、GCC、Borland C \ C ++、MSVCなどのさまざまなコンパイラーで長年安定していました。

C ++の人気はますます高まっていますが、新しいC ++ドメインに多くのものを追加する必要があります(たとえば、Cが必要なすべての機能をカバーできなかったため、最終的にCfrontはAT&Tで放棄されました)。テンプレート機能やコンパイル時のコード生成など、過去から、さまざまなコンパイラベンダーが実際にC ++コンパイラとリンカの実際の実装を別々に行っていました。実際のABIは、さまざまなプラットフォームのC++プログラムとまったく互換性がありません。

人々はまだ実際のプログラムをC++で実装したいと思うかもしれませんが、それでも古いCインターフェイスとABIを通常どおり維持します。ヘッダーファイルはextern "C" {}を宣言する必要があり、コンパイラに互換性のある/古い/シンプル/簡単なCABIを生成するように指示しますコンパイラがC++コンパイラではなくCコンパイラの場合、インターフェイス関数用。

于 2018-12-14T10:04:21.867 に答える