Ist es erforderlich, „extern C“ auch in der Quelldatei hinzuzufügen?

Lesezeit: 9 Minuten

Benutzeravatar von Jay
Jay

Ich habe kürzlich Code gefunden, bei dem extern “C” in der Quelldatei auch für Funktionen hinzugefügt wurde. Sie wurden auch in den Header-Dateien hinzugefügt, in denen sie deklariert wurden.

Ich war davon ausgegangen, dass das Hinzufügen von ‘extern “C” in Header-Dateien ausreicht.

Wo sollten externe “C”-Blöcke hinzugefügt werden?

UPDATE: Angenommen, ich kompiliere meinen C-Code mit einem CPP-Compiler und habe externe “C”-Wächter für alle Funktionen in Header-Dateien hinzugefügt (dh alle meine Funktionen haben ihre Prototypen in Header), aber in Quelldateien habe ich nicht dasselbe hinzugefügt . Wird dies ein Problem verursachen?

  • Auf jeden Fall in den Überschriften. Ich habe einmal an einem Vertrag gearbeitet, bei dem der Codierungsstandard Wächter in der Quelle und nicht im Header enthalten musste. Heilige Kesselplatte, Batman!

    – Greg Bacon

    30. Januar 2010 um 14:57 Uhr


  • @gbacon: Das war die Empfehlung in Lakos’ Buch über umfangreiche C++-Programmierung. Die Idee war, dass Include Guards alle Kopfzeilen lesen mussten. Natürlich erkennt gcc heutzutage Include-Guards und liest nicht die gesamte .h-Datei.

    – David Thornley

    30. Januar 2010 um 15:00 Uhr

  • Was meinst du mit ‘C’ Wachen? Guards oder externe “C”-Blöcke einschließen?

    – bk1e

    30. Januar 2010 um 17:15 Uhr

  • Es handelt sich um externe “C”-Blöcke.

    – Jay

    31. Januar 2010 um 11:32 Uhr

Benutzeravatar von Adam Bowen
Adam Bowen

Da meinst du

extern "C" { ... }

Stilwächter deklarieren diese einige Funktionen als “C”-Verknüpfung und nicht als “C++”-Verknüpfung (die normalerweise eine Reihe zusätzlicher Namensdekorationen enthält, um Dinge wie überladene Funktionen zu unterstützen).

Der Zweck besteht natürlich darin, C++-Code die Schnittstelle mit C-Code zu ermöglichen, der sich normalerweise in einer Bibliothek befindet. Wenn die Header der Bibliothek nicht mit Blick auf C++ geschrieben wurden, dann werden sie die nicht enthalten extern "C" Wächter für C++.

Ein AC-Header, der mit Blick auf C++ geschrieben wurde, enthält etwas in der Art von

#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

um sicherzustellen, dass C++-Programme die richtige Verknüpfung sehen. Allerdings wurden nicht alle Bibliotheken mit Blick auf C++ geschrieben, also muss man das manchmal tun

extern "C" {
#include "myclibrary.h"
}

um die Verknüpfung richtig zu machen. Wenn die Header-Datei von jemand anderem bereitgestellt wird, ist es nicht ratsam, sie zu ändern (weil Sie sie dann nicht einfach aktualisieren können). Daher ist es besser, die Header-Datei mit Ihrem eigenen Guard (möglicherweise in Ihrer eigenen Header-Datei) zu umschließen.

extern "C" ist nicht (AFAIK) ANSI C, kann also ohne die Präprozessor-Wächter nicht in normalen C-Code aufgenommen werden.

Als Antwort auf Ihre Bearbeitung:

Wenn Sie einen C++-Compiler verwenden und eine Funktion als extern „C“ in der Headerdatei deklarieren, müssen Sie diese Funktion nicht auch als extern „C“ in der Implementierungsdatei deklarieren. Aus Abschnitt 7.5 des C++-Standards (Hervorhebung von mir):

Wenn zwei Deklarationen derselben Funktion oder desselben Objekts unterschiedliche Verknüpfungsspezifikationen angeben (d. h. die Verknüpfungsspezifikationen dieser Deklarationen spezifizieren unterschiedliche Zeichenfolgenliterale), ist das Programm falsch formatiert, wenn die Deklarationen in derselben Übersetzungseinheit erscheinen und die eine Definitionsregel gilt, wenn die Deklarationen in unterschiedlichen Übersetzungseinheiten erscheinen. Mit Ausnahme von Funktionen mit C++-Verknüpfung darf eine Funktionsdeklaration ohne Verknüpfungsspezifikation nicht vor der ersten Verknüpfungsspezifikation für diese Funktion stehen. Eine Funktion kann ohne eine Verknüpfungsspezifikation deklariert werden, nachdem eine explizite Verknüpfungsspezifikation gesehen wurde; die explizit in der früheren Deklaration angegebene Verknüpfung wird durch eine solche Funktionsdeklaration nicht beeinflusst.

Ich bin jedoch nicht davon überzeugt, dass dies eine gute Praxis ist, da die Verknüpfungsspezifikationen versehentlich abweichen können (wenn beispielsweise die Header-Datei mit der Verknüpfungsspezifikation nicht in der Implementierungsdatei enthalten ist). Ich denke, es ist besser, in der Implementierungsdatei explizit zu sein.

  • Was ist mit statischen Funktionen? In der Header-Datei wird keine Deklaration vorhanden sein, sollten wir also extern “C” dafür in der Quelldatei hinzufügen? Ich spreche von einem C-Code, der mit dem CPP-Compiler kompiliert wird.

    – Jay

    1. Februar 2010 um 9:55 Uhr

  • Jede Funktion, die Sie außerhalb der aktuellen Kompilierungseinheit verknüpfen müssen, muss die richtige Verknüpfung deklariert haben. Es spielt keine Rolle für eine statische Funktion (dh eine, die nur in der aktuellen c-Datei zu sehen ist), da die Funktion nicht exportiert wird und daher nie verknüpft werden muss.

    – Adam Bowen

    1. Februar 2010 um 10:52 Uhr

  • Ich bekomme das Problem, auf das Sie hingewiesen haben, weil “die Header-Datei mit der Verknüpfungsspezifikation nicht in der Implementierungsdatei enthalten ist”, also stimme ich Ihnen voll und ganz zu, besser explizit extern “C” in die Implementierungsdateien einzufügen, wie Sie es gespeichert haben mir viel Zeit, danke 🙂

    – Moin

    1. April 2020 um 13:11 Uhr

Benutzeravatar von Peter Alexander
Peter Alexander

Sie müssen nur alles einfügen, was in anderen Quelldateien enthalten ist.

Mit einige Redewendungen Sie werden Leute finden, die Quelldateien enthalten.

Sie sollten allen Dateien hinzugefügt werden, die in anderen Dateien enthalten sind.

Normalerweise schließt man keine Quelldateien ein.

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Apologie

Die Frage hat sich geändert, um viel klarer zu sein, worum es ging. Diese Antwort befasste sich mit der ursprünglichen Frage, als es zumindest umstritten war, ob es um Schutzmaßnahmen gegen die mehrfache Aufnahme in Header-Dateien ging – was meine Antwort betrifft. Wenn die Frage damals so klar gewesen wäre wie heute, hätte ich diese Antwort natürlich nicht vorgelegt.


Ursprüngliche Antwort

Nein, es ist nicht notwendig, die Guards auch in den C-Code aufzunehmen.

Wenn die Header-Datei ‚header.h‘ sagt:

#ifndef HEADER_H_INCLUDED
#define HEADER_H_INCLUDED
...
#endif

Dann ist es für eine Quelldatei ‘source.c’ absolut sicher zu sagen:

#include "header.h"

Es ist auch sicher, dass andere Header „header.h“ enthalten.

Die Leute bemerken jedoch, dass das Öffnen und Lesen einer Header-Datei einige Zeit in Anspruch nimmt, was eine Kompilierung verlangsamt. Daher tun die Leute manchmal Dinge wie:

#ifndef HEADER_H_INCLUDED
#include "header.h"
#endif

Das bedeutet, dass, wenn ein anderer in „source.c“ enthaltener Header bereits „header.h“ enthalten hat, „#include“ nicht erneut verarbeitet wird. (Oder, wenn „header.h“ bereits direkt in „source.c“ enthalten ist, obwohl das ein dummes Buglet ist.)

Wenn es also auftritt, ist es wahrscheinlich ein Versuch, die Kompilierungsleistung zu optimieren. Es ist alles andere als klar, dass es Ihnen viel bringt; Moderne C-Präprozessoren sind ziemlich intelligent in Bezug auf das Problem und vermeiden das erneute Einschließen der Datei, wenn sie können. Und es besteht immer das Risiko, dass der Test in „source.c“ einen Tippfehler enthält (vielleicht #ifndef HEARER_H_INCLUDED), in diesem Fall verlangsamt der Test die Kompilierung, weil der Präprozessor die irrelevante Bedingung testet und dann „header.h“ danach einschließt alle. Es ist sicher’; der Header selbst ist geschützt – oder sollte es sein.

Wenn Sie sehen, dass der Code in „source.c“ auch „#define HEADER_H_INCLUDED“ ausführt, dann gibt es Probleme. Das #define muss entweder vor oder nach dem #include stehen, und beides ist nicht gut als allgemeine Technik.

  • Wenn „source.c“ „#define HEADER_H_INCLUDED“ ausführt, bevor „header.h“ eingeschlossen wird, dann wird der Inhalt des Headers nicht eingeschlossen, wenn der Wächter in „header.h“ erscheint. Wenn der Wächter nicht in ‘header.h’ erscheint, dann funktioniert alles OK.
  • Wenn „source.c“ nach dem Einfügen von „header.h“ „#define HEADER_H_INCLUDED“ ausführt, erhalten wir, wenn der Wächter in „header.h“ erscheint, eine gutartige Neudefinition von HEADER_H_INCLUDED. Wenn ‘header.h’ den Guard nicht enthält, aber eine Datei, die ‘header.h’ enthält, sind Sie doch nicht vor Mehrfacheinbindung geschützt.

Beachten Sie, dass der Hauptteil des Headers nach „#define HEADER_H_INCLUDED“ erscheint. Dies ist wiederum ein Schutz, wenn verschachtelte Includes ‘header.h’ enthalten.

Du meinst die ‘extern c’-Präprozessoren? Sie müssen in der Funktionsdefinition enthalten sein und beeinflussen, wie der Funktionsaufruf in der kompilierten Binärdatei gespeichert wird. Es wird nur wirklich benötigt, wenn Sie kompiliertes c++ zusammen mit c verknüpfen, das als C kompiliert ist (im Gegensatz zu c in einer .cpp-Datei).

Benutzeravatar von VoidPointer
VoidPointer

Die “C”-Wächter haben zwei Zwecke:

  1. Wenn Ihr Code kompiliert ist, werden die Funktionen so exportiert, dass ein Nicht-C++-Compiler/Linker sie verwenden kann (keine C++-Namensverfälschung usw.).
  2. Wenn ein C++-Compiler Ihre Header-Dateien verwendet, weiß er, dass er die Symbole auf C-Weise binden sollte, was wiederum sicherstellt, dass das resultierende Programm erfolgreich verknüpft wird. Sie haben keine Bedeutung für einen Nicht-C++-Compiler, aber da die Symbole in (1) im C-Stil generiert wurden, ist dies der gewünschte Effekt.

Da Sie den Header mit den “C”-Guards auch in Ihre Implementierungsdatei aufnehmen, stehen dem Compiler die Informationen darüber zur Verfügung, wie die Symbole zur Kompilierzeit erstellt werden sollen, und der Compiler erstellt die Symbole so, dass sie von einem verwendet werden können Nicht-C++-Compiler. Folglich müssen Sie nur angeben extern “C” in Ihrer Header-Datei, solange die Header-Datei auch von der Implementierungsdatei enthalten ist.

Benutzeravatar von Emil.Thomas
Emil.Thomas

Es ist nicht erforderlich, dass extern in Quelldateien verwendet wird, wenn sie in der Header-Datei verwendet werden und diese Datei von den restlichen Quelldateien eingeschlossen wird.

Soweit ich mich an den Standard erinnere, werden alle Funktionsdeklarationen standardmäßig als “extern” betrachtet, sodass dies nicht explizit angegeben werden muss. Das macht dieses Schlüsselwort nicht nutzlos, da es auch mit Variablen verwendet werden kann (und wenn das der Fall ist – es ist die einzige Lösung, um Verknüpfungsprobleme zu lösen). Aber mit den Funktionen – ja, es ist optional.

Eine etwas ausführlichere Antwort ist, dass Sie damit Variablen verwenden können, die in einer anderen Quellcodedatei kompiliert wurden, aber keinen Speicher für die Variable reservieren. Um also extern zu verwenden, müssen Sie eine Quellcodedatei oder eine Bibliothekseinheit haben, die Speicherplatz für die Variable auf der obersten Ebene enthält (nicht innerhalb von Funktionen). Jetzt können Sie auf diese Variable verweisen, indem Sie eine externe Variable mit demselben Namen in Ihren anderen Quellcodedateien definieren.

Im Allgemeinen sollte die Verwendung einer externen Definition vermieden werden. Sie führen leicht zu unüberschaubarem Code und Fehlern, die schwer zu lokalisieren sind. Natürlich gibt es Beispiele, bei denen andere Lösungen unpraktisch wären, aber sie sind selten. Beispielsweise sind stdin und stdout Makros, die einer externen Array-Variablen vom Typ FILE* in stdin.h zugeordnet sind; Der Speicherplatz für dieses Array befindet sich in einer standardmäßigen C-Bibliothekseinheit.

  • Darum geht es in der Frage nicht. Die Frage bezieht sich auf die Verwendung von {extern “C”} und nicht auf die Verwendung von {extern}

    – Grimmiger Fandango

    17. Januar 2018 um 19:06 Uhr

1396970cookie-checkIst es erforderlich, „extern C“ auch in der Quelldatei hinzuzufügen?

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

Privacy policy