C ++とCを組み合わせる-#ifdef __cplusplusはどのように機能しますか?


319

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

したがって、各Cヘッダーファイルの先頭(インクルードガードの後)には、

#ifdef __cplusplus
extern "C" {
#endif

下部には、

#ifdef __cplusplus
}
#endif

2つの間に、すべてのinclude、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 ++を混合する予定です。すべてのベースをカバーできるようにしたいと思います。



2
簡潔に、これが最良の説明は次のとおりです。 To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed. (私からそれを持っているリンク
anhldbk

C言語の名前を太字にする必要はありません
Edward Karak

回答:


290

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 ++として解釈されるときにプロトタイプが必要であり、Cとして解釈されるときにプロトタイプが必要ではないextern "C"ため、#ifdef __cplusplusチェックが行われます。

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

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

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


10
extern "C++"C ++ヘッダー/コードがCコードの奥深くに閉じ込められている場合の言及に
適してい

1
簡単なCプログラムを書きました。その中に#ifdef __cplusplusブロックを追加し、printf( "__ cplusplus defined \ n");を追加しました。初期化。gccでコンパイルすると「__cplusplus defined」は表示されませんが、g ++でコンパイルすると表示されます。したがって、__ cplusplusはコンパイラがC ++コンパイラであることを意味します(あなたが言ったように)。正しいですか?(「__cplusplusはextern "C"ブロック内で定義する必要がある」と言ったので、__ cplusplusを明示的に定義できますか?
Chan Kim

1
(ほぼ)何でも好きなように定義できるはずですが、の目的は、vsが使用されている__cplusplusかどうかを判断することです。そのため、手動で定義するか、明示的に定義すると、その目的がC++C
損なわ

extern "C"は、コンパイラがソースファイルをどのように表示するかではなく、ヘッダーファイルを表示する方法についてすべてです。CとC ++としてコンパイルした場合、構造体のサイズが異なる可能性があります。もちろん、名前のマングリングがあり、場合によっては他の違いもあります。
Nick

39
  1. extern "C"__cplusplusマクロの有無は変更されません。ラップされた宣言のリンケージと名前のマングリングを変更するだけです。

  2. あなたはextern "C"かなり幸せにブロックを入れ子にすることができます。

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

  4. はい

  5. このようにして、CとC ++を安全に混在させることができます。


.cファイルをC ++としてコンパイルすると、extern "C"ブロック内であってもすべてがC ++コードとしてコンパイルされます。extern "C"コードは(例えば、オペレータがオーバーロードする)Cに依存する機能++呼び出し規約を使用することはできませんが、関数の本体はまだすべてが伴いますと、C ++としてコンパイルされます。
デビッドC.

21

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

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

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

あなたの関数が見つからないために表示されますので、あなたがない場合、リンカエラーが、後に起こるであろうではない持っているのextern「C」宣言のラッパーをし、ヘッダがCおよびC ++ソースの混合物中に含まれています。

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


1
これは不可解な原因で、髪を引っ張ってしまいました。本当にどこかに投稿する必要があります。
Mitchell Currie

3

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

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

C ++がますます一般的になる一方で、新しいC ++ドメインに多くのことを追加する必要があります(たとえば、Cが必要なすべての機能をカバーできなかったため、CfrontはAT&Tで廃止されました)。以下のようなテンプレートの過去からの機能、およびコンパイル時のコード生成、異なるコンパイラベンダーが実際にC ++コンパイラとリンカの実際の実装は別に、実際のABIは、異なるプラットフォームでC ++プログラムには全く互換性がありませんでした。

人々はまだC ++での実際のプログラムを実装するように、まだいつものように、古いCインタフェースとABIを維持する可能性がある、ヘッダファイルを宣言するために持っているのextern「C」{}を、それはコンパイラが伝え互換性/古い/シンプル/簡単なC ABIを生成しますコンパイラーがC ++コンパイラーではなくCコンパイラーである場合のインターフェース関数用。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.