401 Stimmen

Kombination von C++ und C - wie funktioniert #ifdef __cplusplus?

Ich arbeite an einem Projekt, das eine Menge Altlasten enthält. C Code. Wir haben begonnen, in C++ zu schreiben, mit der Absicht, auch den alten Code zu konvertieren. Ich bin ein wenig verwirrt darüber, wie die C und C++ interagieren. Ich verstehe, dass durch die Ummantelung der C Code mit extern "C" wird der C++-Compiler die C Namen des Codes, aber ich bin mir nicht ganz sicher, wie ich das umsetzen soll.

Am Anfang eines jeden C Header-Datei (nach den Include-Guards), haben wir

#ifdef __cplusplus
extern "C" {
#endif

und am Ende schreiben wir

#ifdef __cplusplus
}
#endif

Dazwischen befinden sich alle Includes, Typedefs und Funktionsprototypen. Ich habe ein paar Fragen, um zu sehen, ob ich das richtig verstanden habe:

  1. Wenn ich eine C++-Datei A.hh habe, die enthält eine C Header-Datei B.h, enthält eine weitere C Header-Datei C.h, wie funktioniert das? Ich denke, dass wenn der Compiler in B.h einsteigt, __cplusplus definiert werden, so dass es verpackt den Code mit extern "C" (und __cplusplus wird nicht innerhalb dieses Blocks definiert). Also, wenn es in C.h eintritt, __cplusplus wird nicht definiert und der Code wird nicht in extern "C" . Ist dies korrekt?

  2. Ist etwas falsch mit ein Stück Code zu umhüllen mit extern "C" { extern "C" { .. } } ? Was wird die zweite extern "C" tun?

  3. Wir legen diesen Wrapper nicht um die .c-Dateien, sondern nur um die .h-Dateien. Was passiert also, wenn eine Funktion keinen Prototyp hat? Denkt der Compiler, dass es sich um eine C++-Funktion handelt?

  4. Wir verwenden auch einige Drittanbieter Code, der in C und tut diese Art von Umhüllung nicht hat sie. Jedes Mal, wenn ich eine Kopfzeile aus dieser Bibliothek einbinde, habe ich ein extern "C" um das #include herum. Ist das der richtige Weg, um damit umzugehen damit umzugehen?

  5. Und schließlich: Ist diese Einrichtung eine gute Idee? Gibt es noch etwas, was wir tun sollten? Wir werden Folgendes mischen C und C++ für die absehbare Zukunft, und ich möchte sicherstellen, dass wir alle Grundlagen abdecken.

358voto

Andrew Shelansky Punkte 4652

extern "C" ändert nicht wirklich die Art und Weise, wie der Compiler den Code liest. Wenn Ihr Code in einer .c-Datei steht, wird er als C kompiliert, wenn er in einer .cpp-Datei steht, wird er als C++ kompiliert (es sei denn, Sie machen etwas Seltsames in Ihrer Konfiguration).

Was extern "C" hat Auswirkungen auf die Verknüpfung. Die Namen von C++-Funktionen werden beim Kompilieren verfälscht, was die Überladung erst möglich macht. Der Funktionsname wird auf der Grundlage der Typen und der Anzahl der Parameter geändert, so dass zwei Funktionen mit demselben Namen unterschiedliche Symbolnamen haben werden.

Code innerhalb einer extern "C" ist immer noch C++-Code. Es gibt Einschränkungen, was Sie in einem externen "C"-Block tun können, aber dabei geht es nur um die Verknüpfung. Sie können keine neuen Symbole definieren, die nicht mit C-Verknüpfung erstellt werden können. Das bedeutet zum Beispiel keine Klassen oder Vorlagen.

extern "C" Blöcke nisten gut. Außerdem gibt es extern "C++" wenn Sie hoffnungslos gefangen sind in einem extern "C" Regionen, aber aus Sicht der Sauberkeit ist das keine so gute Idee.

Nun speziell zu Ihren nummerierten Fragen:

Zu #1: __cplusplus bleibt innerhalb von extern "C" Blöcke. Das macht aber nichts, da die Blöcke ordentlich ineinander verschachtelt werden sollten.

Zu #2: __cplusplus wird für jede Kompiliereinheit definiert, die durch den C++-Compiler läuft. Im Allgemeinen bedeutet das .cpp-Dateien und alle Dateien, die von dieser .cpp-Datei eingeschlossen werden. Dieselbe .h (oder .hh oder .hpp oder was auch immer) kann zu verschiedenen Zeiten als C oder C++ interpretiert werden, wenn verschiedene Kompiliereinheiten sie einschließen. Wenn Sie möchten, dass die Prototypen in der .h-Datei auf C-Symbolnamen verweisen, dann müssen sie extern "C" wenn sie als C++ interpretiert werden, und sie sollten keine extern "C" wenn es als C interpretiert wird - daher die #ifdef __cplusplus Kontrolle.

Um Ihre Frage Nr. 3 zu beantworten: Funktionen ohne Prototypen haben eine C++-Verknüpfung, wenn sie sich in .cpp-Dateien und nicht innerhalb einer extern "C" Block. Das ist jedoch in Ordnung, denn wenn sie keinen Prototyp hat, kann sie nur von anderen Funktionen in derselben Datei aufgerufen werden, und dann ist es in der Regel egal, wie die Verknüpfung aussieht, da Sie ohnehin nicht vorhaben, diese Funktion von etwas außerhalb derselben Kompilierungseinheit aufrufen zu lassen.

Zu #4: Sie haben es genau getroffen. Wenn Sie einen Header für Code mit C-Verknüpfung einfügen (z. B. Code, der von einem C-Compiler kompiliert wurde), dann müssen Sie extern "C" den Header - auf diese Weise können Sie mit der Bibliothek linken. (Andernfalls würde Ihr Linker nach Funktionen mit Namen wie _Z1hic als Sie auf der Suche nach void h(int, char)

5: Diese Art der Vermischung ist ein häufiger Grund für die Verwendung von extern "C" und ich sehe nichts Falsches daran, es auf diese Weise zu tun - stellen Sie nur sicher, dass Sie verstehen, was Sie tun.

55voto

Anthony Williams Punkte 64334
  1. extern "C" ändert nichts an der Anwesenheit oder Abwesenheit des __cplusplus Makro. Es ändert lediglich die Verknüpfung und Namensvermischung der umschlossenen Deklarationen.

  2. Sie können verschachteln extern "C" Blöcke ganz glücklich.

  3. Kompilieren Sie Ihre .c Dateien als C++, dann wird alles, was nicht in einer extern "C" Block, und ohne einen extern "C" Prototyp wird wie eine C++-Funktion behandelt. Wenn Sie sie als C kompilieren, wird natürlich alles eine C-Funktion sein.

  4. Ja

  5. Auf diese Weise können Sie C und C++ sicher mischen.

28voto

Andy Dent Punkte 16955

Ein paar Probleme, die mit Andrew Shelanskys ausgezeichneter Antwort kollidieren und denen ich ein wenig widersprechen möchte ändert nicht wirklich die Art und Weise, wie der Compiler den Code liest

Da Ihre Funktionsprototypen als C kompiliert werden, können Sie dieselben Funktionsnamen nicht mit unterschiedlichen Parametern überladen - das ist eines der Hauptmerkmale der Namensmanipulation des Compilers. Es wird als ein Problem bei der Verknüpfung beschrieben, aber das ist nicht ganz richtig - Sie werden sowohl vom Compiler als auch vom Linker Fehler erhalten.

Die Compilerfehler treten auf, wenn Sie versuchen, C++-Funktionen der Prototypendeklaration wie Überladen zu verwenden.

Die Linker-Fehler werden später auftreten, weil Ihre Funktion scheinbar nicht gefunden wird, wenn Sie no haben die extern "C" Wrapper um die Deklarationen und der Header ist in einer Mischung aus C- und C++-Quellcode enthalten.

Ein Grund, die Menschen davon abzuhalten, die C als C++ kompilieren Der Grund für diese Einstellung ist, dass ihr Quellcode nicht mehr portabel ist. Diese Einstellung ist eine Projekteinstellung, und wenn eine .c-Datei in einem anderen Projekt abgelegt wird, wird sie nicht als c++ kompiliert. Mir wäre es lieber, wenn die Leute sich die Zeit nehmen würden, die Dateiendungen in .cpp umzubenennen.

7voto

Bo Zhou Punkte 67

Es geht um die ABI, damit sowohl C- als auch C++-Anwendungen die C-Schnittstellen ohne Probleme nutzen können.

Da die Sprache C sehr einfach ist, war die Codegenerierung viele Jahre lang für verschiedene Compiler, wie GCC, Borland C \C ++, MSVC usw.

Während C++ immer beliebter wird, müssen viele Dinge in die neue C++-Domäne aufgenommen werden (zum Beispiel wurde die Cfront bei AT&T aufgegeben, weil C nicht alle benötigten Funktionen abdecken konnte). Zum Beispiel Vorlage In der Vergangenheit haben die verschiedenen Compilerhersteller die tatsächliche Implementierung von C++-Compiler und Linker getrennt vorgenommen, so dass die tatsächlichen ABIs überhaupt nicht mit dem C++-Programm auf verschiedenen Plattformen kompatibel sind.

Wenn man das eigentliche Programm in C++ implementieren möchte, aber die alte C-Schnittstelle und ABI beibehalten will, muss die Header-Datei wie üblich deklarieren extern "C" {} sagt es dem Compiler kompatible/alte/einfache/einfache C ABI für die Schnittstellenfunktionen erzeugen wenn der Compiler ein C-Compiler und nicht ein C++-Compiler ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X