Warum brauchen wir extern "C"{ #include <foo.h> } in C++?
Warum müssen wir nutzen:
extern "C" {
#include <foo.h>
}
Speziell:
-
Wann sollte man es verwenden?
-
Was an den compiler/linker-Ebene, der von uns verlangt, es zu benutzen?
-
Wie in Bezug auf die Kompilierung/Verlinkung bedeutet dies, die Probleme lösen, die von uns verlangen, es zu benutzen?
- Ich bin mir nicht sicher wie sonst zu setzen. Hast du gelesen über den Titel?
- Diese Frage markiert wurde als Duplikat von Baum mit Augen, die behaupten, ein dupe Aktueller als die Frage, und dann besagt diese Frage wurde vor-das ist schlichtweg falsch. Wenn dieses zumutbar ist (anhand neuerer Beiträge als "duplizieren", um in der Nähe eine post), dann ist dieser Satz muss geändert werden.
Du musst angemeldet sein, um einen Kommentar abzugeben.
C und C++ sind oberflächlich ähnlich, aber jede kompiliert in einen ganz anderen Satz von code. Wenn Sie eine header-Datei mit einem C++ - compiler der compiler rechnet C++ - code. Wenn, jedoch, es ist ein C header, dann der compiler erwartet die Daten in der header-Datei kompiliert werden, um einem bestimmten format—C++ "ABI", oder "Application Binary Interface", also der linker drosseln bis. Dies ist besser zu übergeben C++ Daten auf eine Funktion erwartet der C-Daten.
(Um in die wirklich nitty-gritty C++'s ABI im Allgemeinen 'mangles' die Namen der Funktionen/Methoden, so ruft
printf()
ohne Kennzeichnen den Prototyp einer C-Funktion, die C++ tatsächlich generieren Sie code aufrufen_Zprintf
, plus extra Mist am Ende.)Also: verwenden Sie
extern "C" {...}
beim einbinden einer c-header—so einfach ist das. Ansonsten haben Sie einen Konflikt in kompilierter code, und der linker ersticken. Für die meisten Header, jedoch, Sie brauchen nicht einmal dieextern
weil die meisten system C-Header bereits die Tatsache, dass Sie aufgenommen werden könnten, die von der C++ - code und schonextern
Ihren code.#ifdef __cplusplus extern "C" { #endif
Also, wenn aus einer C++ Datei, in der Sie noch behandelt als ein C-header.extern "C" legt fest, wie die Symbole in die generierte Objekt-Datei benannt werden sollten. Wenn eine Funktion deklariert wird, ohne extern "C", das symbol name in der Objekt-Datei wird in C++ name mangling. Hier ist ein Beispiel.
Test.C so:
Zusammenstellung und Auflistung der Symbole in der Objekt-Datei gibt:
Der foo-Funktion tatsächlich aufgerufen wird "_Z3foov". Dieser string enthält, geben Sie Informationen für den Rückgabetyp und die Parameter, unter anderem. Wenn Sie stattdessen schreiben test.C wie diese:
Dann kompilieren und schauen, Symbole:
Bekommen Sie C-Verknüpfung. Der name des "foo" - Funktion in der Objekt-Datei ist nur "foo", und es hat nicht alle der ausgefallenen Art info, die kommt aus dem name mangling.
Sie umfassen im Allgemeinen eine Kopf-in extern "C" {} wenn der code, der geht mit der es kompiliert wurde mit einem C-compiler, aber Sie versuchen, es zu nennen, die von C++. Wenn Sie dies tun, sagen Sie dem compiler, dass alle Deklarationen in der header C-Verknüpfung. Wenn Sie den link code, den .o-Dateien enthalten Verweise auf "foo", nicht "_Z3fooblah", die hoffentlich entspricht alles, was in der Bibliothek Sie verknüpfen gegen.
Meisten modernen Bibliotheken stellen wird Wachen, die um solche Header so, dass die Symbole erklärt werden mit der richtigen Verknüpfung. z.B. in einer Menge von standard-Kopfzeilen finden Sie:
Stellt dies sicher, dass, wenn C++ - code enthält die header, die Symbole in Ihrem Objekt-Datei übereinstimmen, was in der C-Bibliothek. Sie sollten nur noch extern "C" {} um Ihre C-header, wenn es alt und nicht diese Wachen bereits.
In C++ können Sie unterschiedliche Entitäten, die Freigabe einen Namen. Zum Beispiel hier ist eine Liste von Funktionen, die alle den Namen foo:
A::foo()
B::foo()
C::foo(int)
C::foo(std::string)
Um zwischen Ihnen zu unterscheiden alle, die C++ - compiler erstellen Sie eindeutige Namen für jede in einen Prozess, der als name-mangling oder dekorieren. C-Compiler das nicht zu tun. Darüber hinaus werden bei jedem C++ - compiler können dies tun, ist eine andere Art und Weise.
extern "C" sagt der C++ - compiler nicht ausführen, name-mangling auf den code innerhalb der geschweiften Klammern. Dies ermöglicht Ihnen das aufrufen von C-Funktionen aus C++.
Es hat zu tun mit der Art, wie die verschiedenen Compiler ausführen name-mangling. Ein C++ - compiler wird 'zerfleischen' der name des symbols, das in der header-Datei in einem völlig anderen Weg, als ein C-compiler würde, so, wenn Sie versuchen, eine Verknüpfung, die Sie erhalten würden, einen linker-Fehler zu sagen es waren Symbole fehlen.
Um dies zu beheben, teilen wir dem C++ - compiler für die Ausführung im "C" Modus, so führt es die name manglings in der gleichen Weise, der C-compiler würde. Dass so getan, die linker-Fehler behoben werden.
Beim verknüpfen C libaries in C++ - Objekt-Dateien
C und C++ verwenden verschiedene Systeme für ein symbol zu benennen. Dies teilt dem linker mit dem C-Schema bei der Verknüpfung in der angegebenen Bibliothek.
Verwendung der C-Namensschema ermöglicht Ihnen die Referenz C-Stil-Symbole. Ansonsten hat der linker versuchen würde C++-style-Symbole, die nicht funktionieren würde.
C und C++ haben unterschiedliche Regeln über die Namen der Symbole. Symbole sind, wie der linker weiß, dass der Aufruf der Funktion "openBankAccount" in eine Objekt Datei, die vom compiler erzeugt wird, einen Verweis auf die Funktion, die Sie als "openBankAccount" in einem anderen Objekt-Datei erzeugt, die aus einer anderen source-Datei von der gleichen (oder kompatiblen) - compiler. Dies ermöglicht Ihnen, ein Programm aus mehr als einer Quelle-Datei, die eine Erleichterung bei der Arbeit an einem großen Projekt.
In C die Regel ist sehr einfach, die Symbole sind alle in einem einzigen Namen Platz sowieso. So ist die ganze Zahl "Socken" gespeichert ist, als "Socken" und die Funktion count_socks gespeichert ist, als "count_socks".
Linkers wurden gebaut für C und andere Sprachen wie C mit diesem einfachen symbol Benennung der Regel. So werden Symbole, die in der linker nur einfache strings.
Aber in C++ - die Sprache können Sie namespaces und Polymorphismus und verschiedenen anderen Dingen, die in Konflikt mit so einer einfachen Regel. Alle sechs polymorphe Funktionen nennt sich "add" müssen verschiedene Symbole, oder falsch verwendet werden, die von anderen Objekt-Dateien. Dies geschieht durch "mangeln" (das ist ein Fachbegriff), die die Namen der Symbole.
Beim verknüpfen von C++ - code zu C-Bibliotheken oder code, müssen Sie extern "C" alles, was in C geschrieben ist, wie die header-Dateien für den C-Bibliotheken, zu sagen, Ihr C++ - compiler, dass diese symbol-Namen sind nicht verstümmelt, während der rest von Ihrem C++ - code muss natürlich "verstümmelt" oder es wird nicht funktionieren.
Sollten Sie die Verwendung von extern "C" jederzeit, dass Sie einen header definieren von Funktionen, die Ihren Wohnsitz in eine Datei kompiliert, die von einem C-compiler, verwendet in einer C++ - Datei. (Viele standard-C-Bibliotheken umfassen kann diese Prüfung in Ihrem Header zu machen es einfacher für die Entwickler)
Zum Beispiel, wenn Sie ein Projekt mit 3 Dateien, util.c, util.h, und main.cpp und sowohl die .c und .cpp-Dateien sind kompilierte C++ - compiler (g++, cc, etc) dann ist es nicht wirklich nötig, und kann sogar dazu führen, linker Fehler. Wenn Ihr build-Prozess verwendet eine normale C-compiler für util.c, dann müssen Sie die Verwendung von extern "C", wenn darunter util.h.
Was passiert ist, dass C++ kodiert die Parameter der Funktion in seinem Namen. Dies ist, wie das überladen von Funktionen arbeitet. Alle, die dazu neigt zu geschehen, um eine C-Funktion ist die Zugabe von einem Unterstrich ("_") an den Anfang des namens. Ohne Verwendung von extern "C" wird der linker auf der Suche nach einer Funktion mit dem Namen DoSomething@@int@float (), wenn die Funktion der eigentliche name ist _DoSomething() oder nur DoSomething().
Verwendung von extern "C" löst das obige problem, indem er dem C++ - compiler, der sollte sich für eine Funktion folgt, dass die C-Namenskonvention anstatt C++ ein.
Den C++ - compiler erstellt symbol-Namen, anders als der C-compiler. Also, wenn Sie versuchen, einen Anruf zu tätigen, um eine Funktion, die sich in einer C-Datei kompiliert, wie C-code, die Sie benötigen, zu sagen, die C++ - compiler, der den symbol-Namen, die es zu lösen versuchen, anders Aussehen, als es standardmäßig; andernfalls ist die link-Schritt wird scheitern.
Den
extern "C" {}
Konstrukt weist den compiler nicht ausführen mangeln auf Namen deklariert, die innerhalb der geschweiften Klammern. Normalerweise ist der C++ - compiler "verbessert die Funktion" Namen, so dass Sie Kodieren, geben Sie Informationen über die Argumente und den Rückgabewert; das ist rief der entstellten Namen. Dieextern "C"
- Konstrukt verhindert, dass die mangeln.Es ist in der Regel verwendet, wenn C++ - code aufrufen muss, eine C-Sprache-Bibliothek. Es kann auch verwendet werden, wenn Sie eine C++ - Funktion (aus einer DLL zum Beispiel) bis C-Kunden.
Diese wird verwendet, um die name manglings Probleme. extern C bedeutet, dass die Funktionen sind in einer "flat" C-style API.
Dekompilieren einer
g++
erzeugten Binär-zu sehen, was Los istBewege ich mich in dieser Antwort von: Was ist die Wirkung von extern "C" in C++? denn diese Frage wurde als Duplikat von diesem.
main.cpp
Kompilieren mit GCC 4.8 Linux ELF Ausgabe:
Dekompilieren der symbol-Tabelle:
Die Ausgabe enthält:
Interpretation
Sehen wir, dass:
ef
undeg
gespeichert wurden, in Symbole mit dem gleichen Namen wie im codedie anderen Symbole wurden entstellt. Lassen Sie uns unmangle Sie:
Fazit: beide der folgenden symbol-Typen wurden nicht entstellt:
Ndx = UND
), zur Verfügung gestellt werden, die zur link-oder zur Zeit von einem anderen Objekt-DateiSo müssen Sie
extern "C"
sowohl beim Aufruf:g++
zu erwarten unmangled Symbole produziert vongcc
g++
zu generieren unmangled Symbole fürgcc
zu verwendenDinge, die nicht funktionieren in extern C
Wird es offensichtlich, dass jede C++ - Funktion, die erfordert, dass die name manglings funktioniert nicht innerhalb
extern C
:Minimaler ausführbarer C von C++ Beispiel
Der Vollständigkeit halber und für die newbs gibt, siehe auch: Wie der Einsatz von C-source-Dateien in ein C++ - Projekt?
Aufrufen von C von C++ ist ziemlich einfach: jede C-Funktion hat nur eine mögliche, nicht entstellten symbol, so dass keine zusätzliche Arbeit erforderlich ist.
main.cpp
c.h
c.c
Run:
Ohne
extern "C"
den link nicht mit:weil
g++
erwartet, finden Sie eine entstelltef
, diegcc
nicht produzieren.Beispiel auf GitHub.
Minimal lauffähigen C++ von C Beispiel
Aufrufen von C++ aus ist das ein bisschen schwieriger: wir haben manuell zu erstellen, nicht entstellten Versionen von jeder Funktion, die wir wollen, zu entlarven.
Hier zeigen wir, wie zu setzen C++ - Funktion überlastungen zu C.
main.c
cpp.h
cpp.cpp
Run:
Ohne
extern "C"
schlägt es mit:weil
g++
generiert entstellte Symbole, diegcc
nicht finden können.Beispiel auf GitHub.
Getestet in Ubuntu 18.04.