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

Lesezeit: 8 Minuten

Kombination von C und C wie funktioniert ifdef cplusplus
Dublev

Ich arbeite an einem Projekt, das viel Erbe hat C Code. Wir haben begonnen, in C++ zu schreiben, mit der Absicht, eventuell auch den Legacy-Code zu konvertieren. Ich bin ein wenig verwirrt darüber, wie die C und C++ interagieren. Ich verstehe das unter Umhüllen der C Code mit extern "C" der C++-Compiler wird die nicht verstümmeln C Codenamen, aber ich bin mir nicht ganz sicher, wie ich das implementieren soll.

Also jeweils oben C Header-Datei (nach den Include-Guards) haben wir

#ifdef __cplusplus
extern "C" {
#endif

und ganz unten schreiben wir

#ifdef __cplusplus
}
#endif

Dazwischen haben wir alle unsere Includes, Typedefs und Funktionsprototypen. Ich habe ein paar Fragen, um zu sehen, ob ich das richtig verstehe:

  1. Wenn ich eine C++-Datei A.hh habe, die eine enthält C Header-Datei Bh, enthält eine andere C Header-Datei Ch, wie funktioniert das? Ich denke, wenn der Compiler in Bh einsteigt,
    __cplusplus wird definiert, also wird der Code mit umbrochen extern "C"
    (und __cplusplus wird nicht innerhalb dieses Blocks definiert). Also, wenn es in Ch eintritt,
    __cplusplus wird nicht definiert und der Code wird nicht eingeschlossen
    extern "C". Ist das richtig?

  2. Ist etwas falsch daran, ein Stück Code mit zu verpacken
    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 Code von Drittanbietern, der darin geschrieben ist C, und hat nicht diese Art von Wrapper um sich herum. Jedes Mal, wenn ich einen Header aus dieser Bibliothek einfüge, habe ich eine eingefügt extern "C" rund um das #einschließen. Ist das der richtige Umgang damit?

  5. Abschließend, ist diese Einrichtung eine gute Idee? Gibt es noch etwas, was wir tun sollten? Wir werden mischen C und C++ für die absehbare Zukunft, und ich möchte sicherstellen, dass wir alle unsere Grundlagen abdecken.

1647080411 856 Kombination von C und C wie funktioniert ifdef cplusplus
Andreas Shelansky

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

Was extern "C" wirkt sich auf die Verknüpfung aus. C++-Funktionen haben, wenn sie kompiliert werden, ihre Namen entstellt – das macht das Überladen möglich. Der Funktionsname wird basierend auf den Typen und der Anzahl der Parameter geändert, sodass zwei Funktionen mit demselben Namen unterschiedliche Symbolnamen haben.

Code in einem extern "C" ist immer noch C++-Code. Es gibt Einschränkungen, was Sie in einem externen “C”-Block tun können, aber es geht 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 Templates.

extern "C" Blöcke verschachteln sich gut. Es gibt auch extern "C++" wenn Sie sich hoffnungslos darin gefangen finden extern "C" Regionen, aber aus Sauberkeitssicht keine so gute Idee.

Nun, speziell zu Ihren nummerierten Fragen:

Zu Nr. 1: __cplusplus bleibt innerhalb von definiert extern "C" Blöcke. Dies spielt jedoch keine Rolle, da die Blöcke sauber verschachtelt sein sollten.

Zu Nr. 2: __cplusplus wird für jede Kompilationseinheit definiert, die durch den C++-Compiler ausgeführt wird. Im Allgemeinen bedeutet dies .cpp-Dateien und alle Dateien, die in dieser .cpp-Datei enthalten sind. Dieselbe .h (oder .hh oder .hpp oder what-have-you) könnte zu unterschiedlichen Zeiten als C oder C++ interpretiert werden, wenn verschiedene Kompilierungseinheiten sie enthalten. Wenn Sie möchten, dass die Prototypen in der .h-Datei auf C-Symbolnamen verweisen, müssen sie dies getan haben extern "C" wenn sie als C++ interpretiert werden, und sie sollten es nicht haben extern "C" wenn es als C interpretiert wird – daher die #ifdef __cplusplus Überprüfung.

Um Ihre Frage Nr. 3 zu beantworten: Funktionen ohne Prototypen haben eine C++-Verknüpfung, wenn sie sich in .cpp-Dateien befinden und nicht in einer extern "C" Block. Das ist jedoch in Ordnung, denn wenn es keinen Prototyp hat, kann es nur von anderen Funktionen in derselben Datei aufgerufen werden, und dann ist es Ihnen im Allgemeinen egal, wie die Verknüpfung aussieht, weil Sie nicht vorhaben, diese Funktion zu haben sowieso von irgendetwas außerhalb derselben Kompilationseinheit aufgerufen werden.

Für #4 hast du es genau. Wenn Sie einen Header für Code mit C-Verknüpfung einschließen (z. B. Code, der von einem C-Compiler kompiliert wurde), müssen Sie dies tun extern "C" die Kopfzeile – auf diese Weise können Sie mit der Bibliothek verlinken. (Andernfalls würde Ihr Linker nach Funktionen mit Namen wie suchen _Z1hic als du gesucht hast void h(int, char)

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

  • Gut zum erwähnen extern "C++" wenn Ihr C++-Header/Code tief in C-Code gefangen ist

    – deddebme

    26. April 2017 um 18:17 Uhr

  • Ich habe ein einfaches C-Programm geschrieben. Darin habe ich den Block #ifdef __cplusplus und einen printf(“__cplusplus defined\n”); drin. Wenn ich es mit gcc kompiliere, wird “__cplusplus defined” nicht gedruckt, aber wenn ich es mit g++ kompiliere, wird es gedruckt. Ich denke also, __cplusplus bedeutet, dass der Compiler ein C++-Compiler ist (Sie haben es gesagt). Ist es nicht richtig? (Weil ich gesehen habe, wie Sie sagten: „__cplusplus sollte innerhalb von externen „C“-Blöcken definiert werden“. Können wir __cplusplus explizit definieren?

    – Chan-Kim

    9. August 2017 um 2:30 Uhr


  • Während Sie in der Lage sein sollten, (fast) alles zu definieren, was Sie wollen, ist der springende Punkt __cplusplus ist festzustellen, ob C++ wird verwendet vs Calso widersetzt sich das manuelle/explizite Definieren dem Zweck davon …

    – nurchi

    31. Oktober 2017 um 14:46 Uhr

  • Beim externen “C” geht es in der Tat nicht darum, wie der Compiler die Quelldatei sieht, sondern darum, wie er die Header-Datei sieht. Strukturen können unterschiedliche Größen haben, wenn sie als C vs. C++ kompiliert werden, es gibt natürlich die Namensverstümmelung und möglicherweise auch andere Unterschiede.

    – Nick

    18. Oktober 2018 um 16:21 Uhr

Kombination von C und C wie funktioniert ifdef cplusplus
Anthony Williams

  1. extern "C" ändert nichts an der Anwesenheit oder Abwesenheit von __cplusplus Makro. Es ändert nur die Verknüpfung und Namensverfälschung der umschlossenen Deklarationen.

  2. Sie können nisten extern "C" blockiert ganz glücklich.

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

  4. Jawohl

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

  • Wenn Sie kompilieren .c Dateien als C++, dann wird alles als C++-Code kompiliert, auch wenn es in einer extern "C" Block. Die extern "C" Code kann keine Funktionen verwenden, die von C++-Aufrufkonventionen abhängen (z. B. das Überladen von Operatoren), aber der Hauptteil der Funktion wird immer noch als C++ kompiliert, mit allem, was dazugehört.

    – David C.

    2. Mai 2018 um 14:05 Uhr

Ein paar Fallstricke, die zu Andrew Shelanskys ausgezeichneter Antwort passen und denen man ein wenig widersprechen kann ä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 Namensverstümmelung des Compilers. Es wird als Verknüpfungsproblem beschrieben, aber das ist nicht ganz richtig – Sie erhalten Fehler sowohl vom Compiler als auch vom Linker.

Die Compilerfehler treten auf, wenn Sie versuchen, C++-Features der Prototypdeklaration wie das Überladen zu verwenden.

Die Linker-Fehler treten später auf, da Ihre Funktion ansonsten scheinbar nicht gefunden wird nicht habe den extern “C” Wrapper um Deklarationen und der Header ist in einer Mischung aus C- und C++-Quellen enthalten.

Ein Grund, Menschen davon abzuhalten, das zu verwenden C als C++ kompilieren Einstellung liegt daran, dass ihr Quellcode dadurch nicht mehr portierbar ist. Diese Einstellung ist eine Projekteinstellung, und wenn eine C-Datei in einem anderen Projekt abgelegt wird, wird sie nicht als C++ kompiliert. Ich würde es bevorzugen, wenn sich die Leute die Zeit nehmen, Dateiendungen in .cpp umzubenennen.

  • Das war die kryptische Ursache, mir die Haare auszureißen. Muss eigentlich irgendwo gepostet werden.

    – Mitchell Currie

    16. Juli 2015 um 23:13 Uhr

Es geht um die ABI, damit sowohl C- als auch C++-Anwendungen problemlos C-Schnittstellen verwenden können.

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

Während C++ immer beliebter wird, müssen viele Dinge in die neue C++-Domäne hinzugefügt werden (z. B. wurde Cfront bei AT&T schließlich aufgegeben, weil C nicht alle erforderlichen Funktionen abdecken konnte). Wie zum Beispiel Vorlage Funktion und Codegenerierung zur Kompilierzeit, aus der Vergangenheit haben die verschiedenen Compiler-Anbieter die eigentliche Implementierung von C++-Compiler und -Linker tatsächlich getrennt durchgeführt, die aktuellen ABIs sind überhaupt nicht mit dem C++-Programm auf verschiedenen Plattformen kompatibel.

Die Leute möchten vielleicht immer noch das eigentliche Programm in C++ implementieren, aber immer noch die alte C-Schnittstelle und ABI wie gewohnt behalten, die Header-Datei muss deklariert werden extern “C” {}teilt es dem Compiler mit Kompatibles/altes/einfaches/einfaches C ABI für die Schnittstellenfunktionen generieren wenn der Compiler ein C-Compiler und kein C++-Compiler ist.

993370cookie-checkKombination von C++ und C – wie funktioniert #ifdef __cplusplus?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy