2008-09-15 11 views
117

Warum tun wir verwenden müssen:Warum brauchen wir extern "C" {#include <foo.h>} in C++?

extern "C" { 
#include <foo.h> 
} 

Im Einzelnen:

  • Wann sollten wir es nutzen?

  • Was passiert auf der Compiler/Linker-Ebene, die wir benötigen, um es zu verwenden?

  • Wie in Bezug auf die Kompilierung/Verknüpfung löst dies die Probleme, die uns erfordern, es zu verwenden?

+0

Ich bin verwirrt, was Sie in Ihrer Frage Titel meinen ... können Sie erarbeiten? –

+29

Ich bin mir nicht sicher, wie ich es anders ausdrücken soll. Hast du über den Titel hinaus gelesen? – Landon

Antwort

106

C und C++ sind oberflächlich ähnlich, aber jedes kompiliert zu einem ganz anderen Satz von Code. Wenn Sie eine Headerdatei mit einem C++ - Compiler einbeziehen, erwartet der Compiler C++ - Code. Wenn es sich jedoch um einen C-Header handelt, erwartet der Compiler, dass die in der Header-Datei enthaltenen Daten in ein bestimmtes Format kompiliert werden - das C++ - "ABI" oder das "Application Binary Interface", so dass der Linker aufstockt. Dies ist vorzuziehen, wenn C++ - Daten an eine Funktion übergeben werden, die C-Daten erwartet.

(Um in das wirklich ans Eingemachtes, C++ 's ABI im Allgemeinen ‚Mangeln‘ die Namen ihrer Funktionen/Methoden, so printf() Aufruf ohne den Prototyp als C-Funktion markiert werden, die C++ tatsächlich generieren Code _Zprintf Aufruf plus Extra Mist am Ende.)

Also: extern "C" {...}; verwenden, wenn ac-Header-es ist so einfach. Andernfalls wird der kompilierte Code nicht übereinstimmen und der Linker wird ersticken. Für die meisten Header benötigen Sie jedoch nicht einmal die extern, da die meisten System C-Header bereits für die Tatsache verantwortlich sind, dass sie möglicherweise von C++ - Code und bereits extern ihren Code enthalten sind.

+1

Könnten Sie bitte mehr über ** "die meisten System C-Header werden bereits für die Tatsache, dass sie möglicherweise von C++ - Code enthalten und bereits extern ihren Code." **? –

+3

@BulatM. Sie enthalten etwa so: '#ifdef __cplusplus extern "C"{ # endif' Also, wenn aus einer C++ Datei enthalten sie noch als C-Header behandelt. – Calmarius

+1

Nein ';' benötigt am Ende. Ich kann es nicht ändern, da es erfordert mindestens 6 Zeichen zu ändern, um eine Änderung anzuwenden -, - dumm – danger89

13

Es hat mit der Art und Weise zu tun, wie die verschiedenen Compiler Namens-Mangling durchführen. Ein C++ - Compiler wird den Namen eines aus der Header-Datei exportierten Symbols auf eine völlig andere Weise als ein C-Compiler verändern. Wenn Sie also versuchen, eine Verknüpfung herzustellen, erhalten Sie einen Linker-Fehler, der besagt, dass Symbole fehlen.

Um dies zu beheben, weisen wir den C++ - Compiler an, im "C" -Modus zu laufen, so dass die Namensänderung auf die gleiche Weise wie beim C-Compiler ausgeführt wird. Wenn Sie dies getan haben, sind die Linker-Fehler behoben.

5

Dies wird verwendet, um Probleme mit der Namensänderung zu beheben. extern C bedeutet, dass die Funktionen in einer "flachen" C-Style-API sind.

6

Der C++ - Compiler erstellt Symbolnamen anders als der C-Compiler. Wenn Sie also versuchen, eine Funktion aufzurufen, die sich in einer C-Datei befindet, die als C-Code kompiliert wurde, müssen Sie dem C++ - Compiler mitteilen, dass die Symbolnamen, die aufgelöst werden sollen, anders aussehen als standardmäßig; Andernfalls wird der Link-Schritt fehlschlagen.

18

In C++ können Sie verschiedene Entitäten haben, die einen Namen teilen.Zum Beispiel ist hier eine Liste von Funktionen alle Namen foo:

  • A::foo()
  • B::foo()
  • C::foo(int)
  • C::foo(std::string)

Um zwischen allen, die C++ Compiler zu unterscheiden erstellt für jeden in einem Prozess, der Name Mangling oder Dekoration genannt wird, eindeutige Namen. C-Compiler machen das nicht. Außerdem kann jeder C++ - Compiler dies auf andere Weise tun.

extern "C" weist den C++ - Compiler an, keine Namensänderung am Code innerhalb der geschweiften Klammern vorzunehmen. Dadurch können Sie C-Funktionen innerhalb von C++ aufrufen.

10

C und C++ haben unterschiedliche Regeln für die Namen von Symbolen. Symbole wissen, wie der Linker weiß, dass der Aufruf der Funktion "openBankAccount" in einer vom Compiler erzeugten Objektdatei ein Verweis auf die Funktion ist, die Sie in einer anderen Objektdatei "openBankAccount" genannt haben, die von einer anderen Quelldatei erzeugt wurde. Compiler. Auf diese Weise können Sie ein Programm aus mehr als einer Quelldatei erstellen, was eine Erleichterung bei der Arbeit an einem großen Projekt darstellt.

In C ist die Regel sehr einfach, Symbole sind sowieso alle in einem einzigen Namensraum. Also wird die Ganzzahl "socks" als "socks" gespeichert und die Funktion count_socks wird als "count_socks" gespeichert.

Linker wurden für C und andere Sprachen wie C mit dieser einfachen Symbolbenennungsregel erstellt. Symbole im Linker sind also nur einfache Zeichenfolgen.

Aber in C++ lässt die Sprache Namespaces und Polymorphismus und verschiedene andere Dinge, die im Widerspruch zu einer so einfachen Regel stehen. Alle sechs Ihrer polymorphen Funktionen, die "add" genannt werden, müssen unterschiedliche Symbole haben, oder das falsche wird von anderen Objektdateien verwendet. Dies geschieht durch "Mangeln" (das ist ein technischer Begriff) die Namen von Symbolen. Wenn Sie C++ - Code mit C-Bibliotheken oder -Code verknüpfen, benötigen Sie externes "C", alles in C geschrieben, wie Header-Dateien für die C-Bibliotheken, um Ihrem C++ - Compiler mitzuteilen, dass diese Symbolnamen nicht verändert werden sollen. während der Rest Ihres C++ - Codes natürlich manipuliert werden muss oder nicht funktionieren wird.

10

Wann sollten wir es verwenden?

Wenn Sie C libaries in C++ verknüpfen Objektdateien

Was am Compiler/Linker Ebene geschieht, die uns erfordert, es zu benutzen?

C und C++ verwenden unterschiedliche Schemata für die Symbolbenennung. Dies weist den Linker an, das Schema von C beim Verknüpfen in der gegebenen Bibliothek zu verwenden.

Wie in Bezug auf die Zusammenstellung/Verknüpfung bedeutet dies die Probleme lösen, die uns verlangen, es zu benutzen?

Mithilfe des C-Benennungsschemas können Sie auf Symbole im C-Stil verweisen.Andernfalls würde der Linker C++ - Style-Symbole ausprobieren, die nicht funktionieren würden.

5

Das Konstrukt extern "C" {} weist den Compiler an, keine Namensänderungen in den geschweiften Klammern auszuführen. Normalerweise "erweitert" der C++ - Compiler Funktionsnamen, so dass sie Typinformationen über Argumente und den Rückgabewert codieren. Dies nennt man den Mangled Name. Das extern "C" Konstrukt verhindert das Mangeln.

Sie wird normalerweise verwendet, wenn C++ - Code eine C-Sprachenbibliothek aufrufen muss. Es kann auch verwendet werden, wenn eine C++ - Funktion (z. B. von einer DLL) für C-Clients verfügbar gemacht wird.

104

extern "C" legt fest, wie Symbole in der generierten Objektdatei benannt werden sollen. Wenn eine Funktion ohne extern "C" deklariert wird, wird der Symbolname in der Objektdatei C++ - Name Mangling verwenden. Hier ist ein Beispiel.

Gegeben test.c etwa so:

void foo() { } 

Kompilieren und Auflistung Symbole in der Objektdatei gibt:

$ g++ -c test.C 
$ nm test.o 
0000000000000000 T _Z3foov 
       U __gxx_personality_v0 

Die foo Funktion tatsächlich "_Z3foov" genannt wird. Diese Zeichenfolge enthält unter anderem Typinformationen für den Rückgabetyp und die Parameter. Wenn Sie stattdessen test.c wie folgt schreiben:

extern "C" { 
    void foo() { } 
} 

Dann kompilieren und Symbole aussehen:

$ g++ -c test.C 
$ nm test.o 
       U __gxx_personality_v0 
0000000000000000 T foo 

Sie C-Bindung erhalten. Der Name der "foo" -Funktion in der Objektdatei ist nur "foo", und sie hat nicht alle Informationen über die Phantasie, die von der Namensänderung kommt.

Normalerweise fügen Sie einen Header in extern "C" {} ein, wenn der dazugehörige Code mit einem C-Compiler kompiliert wurde, aber Sie versuchen, ihn aus C++ aufzurufen. Wenn Sie dies tun, sagen Sie dem Compiler, dass alle Deklarationen in der Kopfzeile die C-Verknüpfung verwenden. Wenn Sie Ihren Code verknüpfen, enthalten Ihre O-Dateien Verweise auf "foo", nicht "_Z3fooblah", die hoffentlich mit dem übereinstimmt, was in der Bibliothek vorhanden ist, mit der Sie verlinken.

Die meisten modernen Bibliotheken werden Wächter um solche Header legen, so dass Symbole mit der richtigen Verknüpfung erklärt werden. z.B. in vielen der Standard-Header finden Sie:

#ifdef __cplusplus 
extern "C" { 
#endif 

... declarations ... 

#ifdef __cplusplus 
} 
#endif 

Dies stellt sicher, dass, wenn C++ Code enthält den Header, die Symbole in Ihrer Objektdatei übereinstimmen, was in der C-Bibliothek ist. Sie sollten nur extern "C" {} um Ihren C-Header legen müssen, wenn es alt ist und diese Wächter noch nicht haben.

+2

einige kurze Beispiele sind wirklich gute Dinge! – zhy

6

Sie sollten extern "C" verwenden, wenn Sie einen Header definieren, der Funktionen definiert, die sich in einer Datei befinden, die von einem C-Compiler kompiliert wurde und in einer C++ - Datei verwendet wird. (Viele Standard-C-Bibliotheken können diese Überprüfung in ihren Kopfzeilen enthalten, um es für den Entwickler einfacher zu machen)

Zum Beispiel, wenn Sie ein Projekt mit 3 Dateien haben, util.c, util.h und main.cpp und beide Die .c und .cpp-Dateien werden mit dem C++ - Compiler (g ++, cc, etc) kompiliert, dann wird es nicht wirklich benötigt und kann sogar Linker-Fehler verursachen. Wenn Ihr Build-Prozess einen regulären C-Compiler für util.c verwendet, müssen Sie extern "C" verwenden, wenn Sie util.h einschließen.

Was passiert ist, dass C++ die Parameter der Funktion in seinem Namen codiert. So funktioniert das Überladen von Funktionen.Alles was mit einer C-Funktion passiert, ist das Hinzufügen eines Unterstrichs ("_") zum Anfang des Namens. Ohne Verwendung von extern "C" wird der Linker nach einer Funktion namens DoSomething @@ int @ float() suchen, wenn der tatsächliche Name der Funktion _DoSomething() oder nur DoSomething() ist.

Die Verwendung von extern "C" löst das obige Problem, indem es dem C++ - Compiler mitteilt, dass es nach einer Funktion suchen soll, die der C-Namenskonvention anstelle von C++ folgt.