@Litherum Auf den ersten Blick weist es den Compiler an, diesen Codebereich mit C zu kompilieren, vorausgesetzt, Sie haben einen Cross-Compiler. Dies bedeutet auch, dass Sie eine Cpp-Datei haben, in der Sie diese foo()Funktion haben.
ha9u63ar
1
@ ha9u63ar Es ist "aus dem Kopf". Der gesamte Rest Ihres Kommentars ist ebenfalls falsch. Ich empfehle Ihnen, es zu löschen.
TamaMcGlinn
Antworten:
1559
extern "C" bewirkt, dass ein Funktionsname in C ++ eine 'C'-Verknüpfung hat (der Compiler entstellt den Namen nicht), sodass der Client-C-Code mithilfe einer' C'-kompatiblen Header-Datei, die nur die enthält, eine Verknüpfung zu Ihrer Funktion herstellen (dh diese verwenden) kann Erklärung Ihrer Funktion. Ihre Funktionsdefinition ist in einem Binärformat enthalten (das von Ihrem C ++ - Compiler kompiliert wurde), das der Client-Linker "C" dann mit dem Namen "C" verknüpft.
Da in C ++ Funktionsnamen überladen sind und in C nicht, kann der C ++ - Compiler den Funktionsnamen nicht einfach als eindeutige ID zum Verknüpfen verwenden, sodass der Name durch Hinzufügen von Informationen zu den Argumenten entstellt wird. Der AC-Compiler muss den Namen nicht entstellen, da Sie Funktionsnamen in C nicht überladen können. Wenn Sie angeben, dass eine Funktion in C ++ über eine externe "C" -Verbindung verfügt, fügt der C ++ - Compiler dem verwendeten Namen keine Informationen zu Argumenten / Parametertypen hinzu Verknüpfung.
Damit Sie wissen, können Sie die Verknüpfung "C" zu jeder einzelnen Deklaration / Definition explizit angeben oder einen Block verwenden, um eine Folge von Deklarationen / Definitionen zu gruppieren, um eine bestimmte Verknüpfung zu erhalten:
Wenn Sie sich für die technischen Details interessieren, sind diese in Abschnitt 7.5 des C ++ 03-Standards aufgeführt. Hier eine kurze Zusammenfassung (mit Schwerpunkt auf externem "C"):
extern "C" ist eine Verknüpfungsspezifikation
Jeder Compiler muss eine "C" -Verbindung bereitstellen
Eine Verknüpfungsspezifikation darf nur im Namespace-Bereich auftreten
Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung. Siehe Richard's Kommentar: Nur Funktionsnamen und Variablennamen mit externer Verknüpfung haben eine Sprachverknüpfung
Zwei Funktionstypen mit unterschiedlichen Sprachverknüpfungen sind unterschiedliche Typen, auch wenn sie ansonsten identisch sind
Verknüpfungsspezifikationen verschachteln, innere bestimmt die endgültige Verknüpfung
extern "C" wird für Klassenmitglieder ignoriert
höchstens eine Funktion mit einem bestimmten Namen kann eine "C" -Verknüpfung haben (unabhängig vom Namespace)
extern "C" erzwingt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen). Siehe Richards Kommentar: 'static' inside 'extern "C"' ist gültig; Eine so deklarierte Entität verfügt über eine interne Verknüpfung und daher keine Sprachverknüpfung
Die Verknüpfung von C ++ mit in anderen Sprachen definierten Objekten und mit in C ++ definierten Objekten aus anderen Sprachen ist implementierungsdefiniert und sprachabhängig. Nur wenn die Objektlayoutstrategien von zwei Sprachimplementierungen ähnlich genug sind, kann eine solche Verknüpfung erreicht werden
Der C-Compiler verwendet kein Mangling, wie es C ++ tut. Wenn Sie also die AC-Schnittstelle von einem C ++ - Programm aus aufrufen möchten, müssen Sie eindeutig angeben, dass die C-Schnittstelle "extern c" ist.
Sam Liao
59
@Faisal: Versuchen Sie nicht, Code zu verknüpfen, der mit verschiedenen C ++ - Compilern erstellt wurde, auch wenn die Querverweise alle "extern" C "sind. Es gibt häufig Unterschiede zwischen den Layouts von Klassen oder den Mechanismen, die zur Behandlung von Ausnahmen verwendet werden, oder den Mechanismen, mit denen sichergestellt wird, dass Variablen vor der Verwendung initialisiert werden, oder anderen derartigen Unterschieden. Außerdem benötigen Sie möglicherweise zwei separate C ++ - Laufzeitunterstützungsbibliotheken (eine für jeder Compiler).
Jonathan Leffler
8
'extern "C" erzwingt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen)' ist falsch. 'static' inside 'extern "C"' ist gültig; Eine so deklarierte Entität verfügt über eine interne Verknüpfung und daher keine Sprachverknüpfung.
Richard Smith
14
'Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung' ist ebenfalls falsch. Nur Funktionsnamen und Variablennamen mit externer Verknüpfung haben eine Sprachverknüpfung.
Richard Smith
9
Beachten Sie, dass dies extern "C" { int i; }eine Definition ist. Dies ist möglicherweise nicht das, was Sie beabsichtigt haben, neben der Nichtdefinition von void g(char);. Um es zu einer Nichtdefinition zu machen, müssten Sie extern "C" { extern int i; }. Andererseits macht die Syntax mit einer Deklaration ohne geschweifte Klammern die Deklaration zu einer extern "C" int i;extern "C" { extern int i; }
Nichtdefinition
327
Ich wollte nur ein paar Informationen hinzufügen, da ich sie noch nicht gesehen habe.
Sie werden sehr oft Code in C-Headern sehen, wie folgt:
#ifdef __cplusplus
extern"C"{#endif// all of your legacy C code here#ifdef __cplusplus
}#endif
Damit können Sie diese C-Header-Datei mit Ihrem C ++ - Code verwenden, da das Makro "__cplusplus" definiert wird. Sie können es aber auch weiterhin mit Ihrem alten C-Code verwenden, in dem das Makro NICHT definiert ist, sodass das eindeutige C ++ - Konstrukt nicht angezeigt wird.
Obwohl ich auch C ++ - Code gesehen habe wie:
extern"C"{#include"legacy_C_header.h"}
was ich mir vorstelle, bewirkt fast dasselbe.
Ich bin mir nicht sicher, welcher Weg besser ist, aber ich habe beide gesehen.
Es gibt einen deutlichen Unterschied. Im ersten Fall wird beim Kompilieren dieser Datei mit dem normalen gcc-Compiler ein Objekt generiert, bei dem der Funktionsname nicht entstellt wird. Wenn Sie dann C- und C ++ - Objekte mit dem Linker verknüpfen, werden die Funktionen NICHT gefunden. Sie müssen diese "Legacy-Header" -Dateien wie in Ihrem zweiten Codeblock mit dem Schlüsselwort extern einfügen.
Anne van Rossum
8
@Anne: Der C ++ - Compiler sucht auch nach nicht verwickelten Namen, da er extern "C"im Header angezeigt wird . Es funktioniert super, hat diese Technik oft benutzt.
Ben Voigt
20
@ Anne: Das stimmt nicht, der erste ist auch in Ordnung. Es wird vom C-Compiler ignoriert und hat den gleichen Effekt wie das zweite in C ++. Dem Compiler ist es egal, ob er extern "C"vor oder nach dem Einfügen des Headers auftritt. Bis es den Compiler erreicht, ist es sowieso nur ein langer Strom von vorverarbeitetem Text.
Ben Voigt
8
@ Anne, nein, ich denke, Sie wurden von einem anderen Fehler in der Quelle betroffen, weil das, was Sie beschreiben, falsch ist. Keine Version von g++hat dies in den letzten 17 Jahren für irgendein Ziel zu irgendeinem Zeitpunkt falsch verstanden. Der springende Punkt des ersten Beispiels ist, dass es keine Rolle spielt, ob Sie einen C- oder C ++ - Compiler verwenden. Für die Namen im extern "C"Block wird keine Namensverknüpfung durchgeführt .
Jonathan Wakely
7
"welches ist besser" - sicher ist die erste Variante besser: Sie ermöglicht es, den Header ohne weitere Anforderungen direkt in C- und C ++ - Code aufzunehmen. Der zweite Ansatz ist eine Problemumgehung für C-Header, bei denen der Autor die C ++ - Schutzvorrichtungen vergessen hat (kein Problem, wenn diese jedoch später hinzugefügt werden, werden verschachtelte externe "C" -Deklarationen akzeptiert ...).
Aconcagua
267
Dekompilieren Sie eine g++generierte Binärdatei, um zu sehen, was los ist
main.cpp
void f(){}void g();extern"C"{void ef(){}void eg();}/* Prevent g and eg from being optimized away. */void h(){ g(); eg();}
Kompilieren und disassemblieren Sie die generierte ELF- Ausgabe:
8:00000000000000007 FUNC GLOBAL DEFAULT 1 _Z1fv
9:00000000000000077 FUNC GLOBAL DEFAULT 1 ef
10:000000000000000e17 FUNC GLOBAL DEFAULT 1 _Z1hv
11:00000000000000000 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
12:00000000000000000 NOTYPE GLOBAL DEFAULT UND _Z1gv
13:00000000000000000 NOTYPE GLOBAL DEFAULT UND eg
Deutung
Wir sehen das:
efund egwurden in Symbolen mit dem gleichen Namen wie im Code gespeichert
Die anderen Symbole wurden verstümmelt. Lassen Sie uns sie entwirren:
Schlussfolgerung: Beide der folgenden Symboltypen wurden nicht entstellt:
definiert
deklariert, aber undefiniert ( Ndx = UND), zur Verknüpfung oder Laufzeit aus einer anderen Objektdatei bereitzustellen
Sie benötigen also extern "C"beide, wenn Sie anrufen:
C aus C ++: Sagen Sie g++, dass Sie entwirrte Symbole erwarten sollen, die von erzeugt werdengcc
C ++ aus C: g++Weisen Sie an, nicht verschränkte Symbole für gccdie Verwendung zu generieren
Dinge, die in extern C nicht funktionieren
Es wird offensichtlich, dass jede C ++ - Funktion, die eine Namensveränderung erfordert, im Inneren nicht funktioniert extern C:
extern"C"{// Overloading.// error: declaration of C function ‘void f(int)’ conflicts withvoid f();void f(int i);// Templates.// error: template with C linkagetemplate<class C>void f(C i){}}
Das Aufrufen von C aus C ++ ist ziemlich einfach: Jede C-Funktion verfügt nur über ein mögliches nicht verstümmeltes Symbol, sodass keine zusätzliche Arbeit erforderlich ist.
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++
* because C does not know what this extern "C" thing is. */#ifdef __cplusplus
extern"C"{#endifint f();#ifdef __cplusplus
}#endif#endif
Das Aufrufen von C ++ von C aus ist etwas schwieriger: Wir müssen nicht entstellte Versionen jeder Funktion, die wir verfügbar machen möchten, manuell erstellen.
Hier zeigen wir, wie C ++ - Funktionsüberladungen C ausgesetzt werden.
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.int f(int i);int f(float i);extern"C"{#endifint f_int(int i);int f_float(float i);#ifdef __cplusplus
}#endif#endif
cpp.cpp
#include"cpp.h"int f(int i){return i +1;}int f(float i){return i +2;}int f_int(int i){return f(i);}int f_float(float i){return f(i);}
Beste Antwort, da Sie 1) ausdrücklich erwähnen, dass extern "C" {Sie nicht entwirrte C-Funktionen aus C ++ - Programmen heraus aufrufen können , sowie entwirrte C ++ - Funktionen aus C ++ - Programmen heraus , die andere Antworten nicht so offensichtlich machen, und 2) weil Sie unterschiedliche Beispiele dafür zeigen jeder. Vielen Dank!
Gabriel Staples
3
Ich mag diese Antwort sehr
Selbststart
4
Zweifellos
1
@JaveneCPPMcGowan Warum denkst du, ich hätte einen C ++ - Lehrer? :-)
Ciro Santilli 1 病毒 审查 六四 事件 法轮功
205
In jedem C ++ - Programm werden alle nicht statischen Funktionen in der Binärdatei als Symbole dargestellt. Diese Symbole sind spezielle Textzeichenfolgen, die eine Funktion im Programm eindeutig identifizieren.
In C entspricht der Symbolname dem Funktionsnamen. Dies ist möglich, weil in C keine zwei nicht statischen Funktionen denselben Namen haben können.
Da C ++ eine Überladung zulässt und viele Funktionen aufweist, die C nicht bietet - wie Klassen, Elementfunktionen, Ausnahmespezifikationen -, kann der Funktionsname nicht einfach als Symbolname verwendet werden. Um dies zu lösen, verwendet C ++ das sogenannte Name Mangling, das den Funktionsnamen und alle erforderlichen Informationen (wie die Anzahl und Größe der Argumente) in eine seltsam aussehende Zeichenfolge umwandelt, die nur vom Compiler und Linker verarbeitet wird.
Wenn Sie also eine Funktion als extern C angeben, führt der Compiler keine Namensverwaltung durch und kann direkt auf sie zugreifen, indem sein Symbolname als Funktionsname verwendet wird.
Dies ist praktisch, wenn Sie solche Funktionen verwenden dlsym()und dlopen()aufrufen.
Was meinst du mit praktisch? Ist Symbolname = Funktionsname würde den an dlsym übergebenen Symbolnamen bekannt machen oder etwas anderes?
Fehler
1
@ Fehler: ja. Im allgemeinen Fall ist es im Wesentlichen unmöglich, eine gemeinsam genutzte C ++ - Bibliothek mit nur einer Header-Datei zu öffnen () und die richtige Funktion zum Laden auszuwählen. (Auf x86 gibt es eine veröffentlichte Spezifikation zur Namensverfälschung in Form des Itanium ABI, die alle mir bekannten x86-Compiler zum Verfälschen von C ++ - Funktionsnamen verwenden, aber nichts in der Sprache erfordert dies.)
Jonathan Tomer
52
C ++ verwirrt Funktionsnamen, um eine objektorientierte Sprache aus einer prozeduralen Sprache zu erstellen
Die meisten Programmiersprachen bauen nicht auf vorhandenen Programmiersprachen auf. C ++ baut auf C auf und ist darüber hinaus eine objektorientierte Programmiersprache, die aus einer prozeduralen Programmiersprache aufgebaut ist. Aus diesem Grund gibt es C ++ - Ausdrücke, extern "C"die Abwärtskompatibilität mit C bieten.
Schauen wir uns das folgende Beispiel an:
#include<stdio.h>// Two functions are defined with the same name// but have different parametersvoid printMe(int a){
printf("int: %i\n", a);}void printMe(char a){
printf("char: %c\n", a);}int main(){
printMe("a");
printMe(1);return0;}
Der AC-Compiler kompiliert das obige Beispiel nicht, da dieselbe Funktion printMezweimal definiert wird (obwohl sie unterschiedliche Parameter int avs haben char a).
Ein C ++ - Compiler kompiliert das obige Beispiel. Es ist egal, dass printMezweimal definiert wird.
g ++ -o printMe printMe.c && ./printMe;
Dies liegt daran, dass ein C ++ - Compiler Funktionen implizit basierend auf ihren Parametern umbenennt ( mangelt ). In C wurde diese Funktion nicht unterstützt. Wenn C ++ jedoch über C erstellt wurde, war die Sprache objektorientiert konzipiert und musste die Fähigkeit unterstützen, verschiedene Klassen mit gleichnamigen Methoden (Funktionen) zu erstellen und Methoden ( Methodenüberschreibung ) basierend auf verschiedenen Methoden zu überschreiben Parameter.
extern "C" sagt "C-Funktionsnamen nicht entstellen"
Stellen Sie sich jedoch vor, wir haben eine Legacy-C-Datei mit dem Namen "parent.c", deren includeFunktionsnamen aus anderen Legacy-C-Dateien "parent.h", "child.h" usw. stammen. Wenn die Legacy-Datei "parent.c" ausgeführt wird Durch einen C ++ - Compiler werden die Funktionsnamen entstellt und stimmen nicht mehr mit den in "parent.h", "child.h" usw. angegebenen Funktionsnamen überein. Daher müssten auch die Funktionsnamen in diesen externen Dateien verwendet werden verstümmelt werden. Das Mangeln von Funktionsnamen in einem komplexen C-Programm mit vielen Abhängigkeiten kann zu fehlerhaftem Code führen. Daher kann es zweckmäßig sein, ein Schlüsselwort anzugeben, das den C ++ - Compiler anweist, einen Funktionsnamen nicht zu entstellen.
Das extern "C"Schlüsselwort weist einen C ++ - Compiler an, C-Funktionsnamen nicht zu entstellen (umzubenennen).
können wir nicht verwenden, extern "C"wenn wir nur eine dllDatei haben? Ich meine, wenn wir keine Header-Datei haben und nur eine Quelldatei (nur Implementierungen) und die Verwendung ihrer Funktion über den Funktionszeiger. In diesem Zustand haben wir nur Funktionen verwendet (unabhängig von ihrem Namen).
BattleTested
@tfmontague, für mich hast du es richtig gemacht! gerade im Kopf.
Artanis Zeratul
29
Kein C-Header kann mit C ++ kompatibel gemacht werden, indem lediglich externes "C" eingeschlossen wird. Wenn Bezeichner in einem C-Header mit C ++ - Schlüsselwörtern in Konflikt stehen, beschwert sich der C ++ - Compiler darüber.
Zum Beispiel habe ich gesehen, dass der folgende Code in einem g ++ fehlschlägt:
extern"C"{struct method {intvirtual;};}
Ein bisschen macht Sinn, ist aber etwas zu beachten, wenn Sie C-Code nach C ++ portieren.
extern "C"bedeutet, die C-Verknüpfung zu verwenden, wie in anderen Antworten beschrieben. Es bedeutet nicht, "den Inhalt als C zu kompilieren" oder so. int virtual;ist in C ++ ungültig und die Angabe einer anderen Verknüpfung ändert daran nichts.
MM
1
... oder im Allgemeinen wird kein Code mit Syntaxfehlern kompiliert.
Valentin Heinitz
4
@ValentinHeinitz natürlich, obwohl die Verwendung von "virtual" als Bezeichner in C kein Syntaxfehler ist. Ich wollte nur darauf hinweisen, dass Sie in C ++ keinen C-Header automatisch verwenden können, indem Sie externes "C" darum setzen.
Sander Mertens
28
Es ändert die Verknüpfung einer Funktion so, dass die Funktion von C aus aufgerufen werden kann. In der Praxis bedeutet dies, dass der Funktionsname nicht entstellt wird .
Verstümmelt ist der allgemein verwendete Begriff ... Glauben Sie nicht, dass ich jemals "dekoriert" mit dieser Bedeutung gesehen habe.
Matthew Scharley
1
Microsoft verwendet (zumindest teilweise) in seiner Dokumentation eher dekorierte als verstümmelte. Sie benennen sogar ihr Werkzeug, um einen Namen zu entdekorieren (auch bekannt als Entflechten) undname.
René Nyffenegger
20
Es weist den C ++ - Compiler an, beim Verknüpfen die Namen dieser Funktionen in einem C-Stil nachzuschlagen, da die Namen der in C und C ++ kompilierten Funktionen während der Verknüpfungsphase unterschiedlich sind.
extern "C" soll von einem C ++ - Compiler erkannt werden und den Compiler darüber informieren, dass die angegebene Funktion im C-Stil kompiliert wird (oder werden soll). Während der Verknüpfung wird auf die korrekte Version der Funktion von C verwiesen.
Ich habe 'extern "C"' zuvor für DLL-Dateien (Dynamic Link Library) verwendet, um usw. die main () -Funktion "exportierbar" zu machen, damit sie später in einer anderen ausführbaren Datei von DLL verwendet werden kann. Vielleicht kann ein Beispiel dafür nützlich sein, wo ich es früher verwendet habe.
DLL
#include<string.h>#include<windows.h>usingnamespace std;#define DLL extern"C" __declspec(dllexport)//I defined DLL for dllexport function
DLL main (){MessageBox(NULL,"Hi from DLL","DLL",MB_OK);}
EXE
#include<string.h>#include<windows.h>usingnamespace std;typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dllFunction mainDLLFunc;//make a variable for function placeholderint main(){char winDir[MAX_PATH];//will hold path of above dllGetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe
strcat(winDir,"\\exmple.dll");//concentrate dll name with path
HINSTANCE DLL =LoadLibrary(winDir);//load example dllif(DLL==NULL){FreeLibrary((HMODULE)DLL);//if load fails exitreturn0;}
mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL,"main");//defined variable is used to assign a function from dll//GetProcAddress is used to locate function with pre defined extern name "DLL"//and matcing function nameif(mainDLLFunc==NULL){FreeLibrary((HMODULE)DLL);//if it fails exitreturn0;}
mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL);}
Schwindel. extern "C"und __declspec(dllexport)sind nicht verwandt. Ersteres steuert die Symboldekoration, letzteres ist für die Erstellung eines Exporteintrags verantwortlich. Sie können ein Symbol auch mit C ++ - Namensdekoration exportieren. Abgesehen davon, dass der Punkt dieser Frage völlig verfehlt wurde, gibt es auch andere Fehler im Codebeispiel. Zum einen maindeklariert der Export aus Ihrer DLL keinen Rückgabewert. Oder Konvention nennen. Beim Importieren weisen Sie eine zufällige Aufrufkonvention zu ( WINAPI) und verwenden das falsche Symbol für 32-Bit-Builds (sollte _mainoder sein _main@0). Entschuldigung, -1.
Unsichtbarer
1
Das wiederholte sich nur, dass Sie nicht wissen, was Sie tun, aber es auf diese Weise zu tun, scheint für Sie zu funktionieren, für eine unbekannte Liste von Zielplattformen. Sie haben die Probleme, die ich in meinem vorherigen Kommentar angesprochen habe, nicht angesprochen. Dies ist immer noch eine Abwertung, da es völlig falsch ist (es gibt noch mehr, die nicht in einen einzigen Kommentar passen).
Unsichtbar
1
Das Posten einer Antwort auf Stack Overflow impliziert, dass Sie wissen, was Sie tun. Dies wird erwartet. Was Ihren Versuch betrifft, "eine Stapelbeschädigung beim Ausführen zu verhindern" : Ihre Funktionssignatur gibt einen Rückgabewert vom Typ an void*, aber Ihre Implementierung gibt nichts zurück. Das wird wirklich gut fliegen ...
Unsichtbar
1
Wenn Sie etwas implementieren, das durch reines Glück zu funktionieren scheint , dann wissen Sie eindeutig nicht , was Sie tun (Ihre "funktionierende" Stichprobe fällt in diese Kategorie). Es ist undefiniertes Verhalten, und es scheint eine gültige Form von undefiniertem Verhalten zu sein. Es ist immer noch undefiniert. Ich würde es sehr begrüßen, wenn Sie in Zukunft mehr Sorgfalt walten lassen würden. Ein Teil davon könnte darin bestehen, diese vorgeschlagene Antwort zu löschen.
Unsichtbar
1
Sie interpretieren eine Funktion, die nichts zurückgibt, als eine Funktion, die einen Zeiger zurückgibt. Es ist reines Glück, dass x86 in Bezug auf nicht übereinstimmende Funktionssignaturen und insbesondere Rückgabewerte vom integralen Typ sehr nachsichtig ist. Ihr Code funktioniert nur zufällig. Wenn Sie nicht einverstanden sind, müssen Sie erklären, warum Ihr Code zuverlässig funktioniert.
Unsichtbar
5
extern "C"ist eine Verknüpfungsspezifikation, mit der C-Funktionen in den Cpp-Quelldateien aufgerufen werden . Wir können C-Funktionen aufrufen, Variablen schreiben und Header einschließen . Die Funktion wird in einer externen Entität deklariert und außerhalb definiert. Syntax ist
Typ 1:
extern"language"function-prototype
Typ 2:
extern"language"{function-prototype
};
z.B:
#include<iostream>usingnamespace std;extern"C"{#include<stdio.h>// Include C Headerint n;// Declare a Variablevoid func(int,int);// Declare a function (function prototype)}int main(){
func(int a,int b);// Calling function . . .return0;}// Function definition . . .void func(int m,int n){////}
Diese Antwort ist für die ungeduldigen / zu befolgenden Fristen, nur ein Teil / eine einfache Erklärung ist unten:
In C ++ können Sie denselben Namen in der Klasse durch Überladen haben (zum Beispiel, da alle denselben Namen nicht wie besehen aus der DLL exportiert werden können usw.). Die Lösung für diese Probleme besteht darin, dass sie in verschiedene Zeichenfolgen (sogenannte Symbole) konvertiert werden ), Symbole berücksichtigen den Namen der Funktion, auch die Argumente, so dass jede dieser Funktionen auch mit demselben Namen eindeutig identifiziert werden kann (auch als Name Mangling bezeichnet).
In C gibt es keine Überladung. Der Funktionsname ist eindeutig (daher ist keine separate Zeichenfolge zum eindeutigen Identifizieren des Funktionsnamens erforderlich, daher ist das Symbol der Funktionsname selbst.)
Also
in C ++, mit dem Namen Mangeln eindeutig Identitäten jede Funktion
in C, auch ohne Namen Mangeln eindeutig Identitäten jede Funktion
Um das Verhalten von C zu ändern ++, das heißt, zu spezifizieren , dass Namen Mangeln nicht sollte für eine bestimmte Funktion geschehen, können Sie extern „C“ vor dem Funktionsnamen, für welche Gründe auch immer, wie eine Funktion mit einem bestimmten Namen aus einer DLL exportieren , zur Verwendung durch seine Kunden.
Lesen Sie andere Antworten, um detailliertere / korrektere Antworten zu erhalten.
Beim Mischen von C und C ++ (dh a. Aufrufen der C-Funktion von C ++ und b. Aufrufen der C ++ - Funktion von C) verursacht das Mangeln von C ++ - Namen Verknüpfungsprobleme. Technisch gesehen tritt dieses Problem nur auf, wenn die Angerufenenfunktionen bereits mit dem entsprechenden Compiler in Binärdateien (höchstwahrscheinlich eine * .a-Bibliotheksdatei) kompiliert wurden.
Wir müssen also externes "C" verwenden, um das Mangeln von Namen in C ++ zu deaktivieren.
Ohne mit anderen guten Antworten in Konflikt zu geraten, werde ich ein wenig von meinem Beispiel hinzufügen.
Was genau der C ++ - Compiler tut: Er entstellt die Namen im Kompilierungsprozess, daher müssen wir den Compiler anweisen, dieC Implementierung speziell zu behandeln .
Wenn wir C ++ - Klassen erstellen und hinzufügen extern "C", teilen wir unserem C ++ - Compiler mit, dass wir die C-Aufrufkonvention verwenden.
Grund (wir rufen die C-Implementierung aus C ++ auf): Entweder möchten wir die C-Funktion aus C ++ aufrufen oder die C ++ - Funktion aus C aufrufen (C ++ - Klassen ... usw. funktionieren in C nicht).
Willkommen bei Stack Overflow. Wenn Sie sich entscheiden, eine ältere Frage zu beantworten, die gut etablierte und korrekte Antworten enthält, erhalten Sie möglicherweise keine Gutschrift, wenn Sie spät am Tag eine neue Antwort hinzufügen. Wenn Sie einige unverwechselbare neue Informationen haben oder davon überzeugt sind, dass die anderen Antworten alle falsch sind, fügen Sie auf jeden Fall eine neue Antwort hinzu, aber "noch eine Antwort", die die gleichen grundlegenden Informationen lange nach dem Stellen der Frage enthält, ist normalerweise gewonnen. " Ich verdiene dir nicht viel Kredit. Ehrlich gesagt glaube ich nicht, dass diese Antwort etwas Neues enthält.
Jonathan Leffler
Nun, ich hätte mich an deinen Punkt erinnern sollen - okay
Susobhan Das
-1
Eine von einem C-Compiler kompilierte Funktion void f () und eine von einem C ++ - Compiler kompilierte Funktion void f () sind nicht dieselbe Funktion. Wenn Sie diese Funktion in C geschrieben und dann versucht haben, sie von C ++ aus aufzurufen, sucht der Linker nach der C ++ - Funktion und findet die C-Funktion nicht.
extern "C" teilt dem C ++ - Compiler mit, dass Sie eine Funktion haben, die vom C-Compiler kompiliert wurde. Sobald Sie ihm mitteilen, dass es vom C-Compiler kompiliert wurde, weiß der C ++ - Compiler, wie er es korrekt aufruft.
Außerdem kann der C ++ - Compiler eine C ++ - Funktion so kompilieren, dass der C-Compiler sie aufrufen kann. Diese Funktion wäre offiziell eine C-Funktion, aber da sie vom C ++ - Compiler kompiliert wird, kann sie alle C ++ - Funktionen verwenden und verfügt über alle C ++ - Schlüsselwörter.
Der C ++ - Compiler kann eine extern "C"Funktion kompilieren - und (unter bestimmten Einschränkungen) kann sie durch Code aufgerufen werden, der von einem C-Compiler kompiliert wurde.
foo()
Funktion haben.Antworten:
extern "C" bewirkt, dass ein Funktionsname in C ++ eine 'C'-Verknüpfung hat (der Compiler entstellt den Namen nicht), sodass der Client-C-Code mithilfe einer' C'-kompatiblen Header-Datei, die nur die enthält, eine Verknüpfung zu Ihrer Funktion herstellen (dh diese verwenden) kann Erklärung Ihrer Funktion. Ihre Funktionsdefinition ist in einem Binärformat enthalten (das von Ihrem C ++ - Compiler kompiliert wurde), das der Client-Linker "C" dann mit dem Namen "C" verknüpft.
Da in C ++ Funktionsnamen überladen sind und in C nicht, kann der C ++ - Compiler den Funktionsnamen nicht einfach als eindeutige ID zum Verknüpfen verwenden, sodass der Name durch Hinzufügen von Informationen zu den Argumenten entstellt wird. Der AC-Compiler muss den Namen nicht entstellen, da Sie Funktionsnamen in C nicht überladen können. Wenn Sie angeben, dass eine Funktion in C ++ über eine externe "C" -Verbindung verfügt, fügt der C ++ - Compiler dem verwendeten Namen keine Informationen zu Argumenten / Parametertypen hinzu Verknüpfung.
Damit Sie wissen, können Sie die Verknüpfung "C" zu jeder einzelnen Deklaration / Definition explizit angeben oder einen Block verwenden, um eine Folge von Deklarationen / Definitionen zu gruppieren, um eine bestimmte Verknüpfung zu erhalten:
Wenn Sie sich für die technischen Details interessieren, sind diese in Abschnitt 7.5 des C ++ 03-Standards aufgeführt. Hier eine kurze Zusammenfassung (mit Schwerpunkt auf externem "C"):
Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung.Siehe Richard's Kommentar: Nur Funktionsnamen und Variablennamen mit externer Verknüpfung haben eine Sprachverknüpfungextern "C" erzwingt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen).Siehe Richards Kommentar: 'static' inside 'extern "C"' ist gültig; Eine so deklarierte Entität verfügt über eine interne Verknüpfung und daher keine Sprachverknüpfungquelle
extern "C" { int i; }
eine Definition ist. Dies ist möglicherweise nicht das, was Sie beabsichtigt haben, neben der Nichtdefinition vonvoid g(char);
. Um es zu einer Nichtdefinition zu machen, müssten Sieextern "C" { extern int i; }
. Andererseits macht die Syntax mit einer Deklaration ohne geschweifte Klammern die Deklaration zu einerextern "C" int i;
extern "C" { extern int i; }
Ich wollte nur ein paar Informationen hinzufügen, da ich sie noch nicht gesehen habe.
Sie werden sehr oft Code in C-Headern sehen, wie folgt:
Damit können Sie diese C-Header-Datei mit Ihrem C ++ - Code verwenden, da das Makro "__cplusplus" definiert wird. Sie können es aber auch weiterhin mit Ihrem alten C-Code verwenden, in dem das Makro NICHT definiert ist, sodass das eindeutige C ++ - Konstrukt nicht angezeigt wird.
Obwohl ich auch C ++ - Code gesehen habe wie:
was ich mir vorstelle, bewirkt fast dasselbe.
Ich bin mir nicht sicher, welcher Weg besser ist, aber ich habe beide gesehen.
quelle
extern "C"
im Header angezeigt wird . Es funktioniert super, hat diese Technik oft benutzt.extern "C"
vor oder nach dem Einfügen des Headers auftritt. Bis es den Compiler erreicht, ist es sowieso nur ein langer Strom von vorverarbeitetem Text.g++
hat dies in den letzten 17 Jahren für irgendein Ziel zu irgendeinem Zeitpunkt falsch verstanden. Der springende Punkt des ersten Beispiels ist, dass es keine Rolle spielt, ob Sie einen C- oder C ++ - Compiler verwenden. Für die Namen imextern "C"
Block wird keine Namensverknüpfung durchgeführt .Dekompilieren Sie eine
g++
generierte Binärdatei, um zu sehen, was los istmain.cpp
Kompilieren und disassemblieren Sie die generierte ELF- Ausgabe:
Die Ausgabe enthält:
Deutung
Wir sehen das:
ef
undeg
wurden in Symbolen mit dem gleichen Namen wie im Code gespeichertDie anderen Symbole wurden verstümmelt. Lassen Sie uns sie entwirren:
Schlussfolgerung: Beide der folgenden Symboltypen wurden nicht entstellt:
Ndx = UND
), zur Verknüpfung oder Laufzeit aus einer anderen Objektdatei bereitzustellenSie benötigen also
extern "C"
beide, wenn Sie anrufen:g++
, dass Sie entwirrte Symbole erwarten sollen, die von erzeugt werdengcc
g++
Weisen Sie an, nicht verschränkte Symbole fürgcc
die Verwendung zu generierenDinge, die in extern C nicht funktionieren
Es wird offensichtlich, dass jede C ++ - Funktion, die eine Namensveränderung erfordert, im Inneren nicht funktioniert
extern C
:Minimal ausführbares C aus C ++ - Beispiel
Der Vollständigkeit halber und für die Neulinge da draußen siehe auch: Wie verwende ich C-Quelldateien in einem C ++ - Projekt?
Das Aufrufen von C aus C ++ ist ziemlich einfach: Jede C-Funktion verfügt nur über ein mögliches nicht verstümmeltes Symbol, sodass keine zusätzliche Arbeit erforderlich ist.
main.cpp
CH
cc
Lauf:
Ohne
extern "C"
den Link schlägt fehl mit:denn
g++
erwartet, eine verstümmelte zu findenf
, diegcc
nicht produziert hat.Beispiel auf GitHub .
Minimal ausführbares C ++ aus C-Beispiel
Das Aufrufen von C ++ von C aus ist etwas schwieriger: Wir müssen nicht entstellte Versionen jeder Funktion, die wir verfügbar machen möchten, manuell erstellen.
Hier zeigen wir, wie C ++ - Funktionsüberladungen C ausgesetzt werden.
Haupt c
cpp.h.
cpp.cpp
Lauf:
Ohne
extern "C"
scheitert es mit:weil
g++
erzeugte verstümmelte Symbole, diegcc
nicht finden können.Beispiel auf GitHub .
Getestet in Ubuntu 18.04.
quelle
extern "C" {
Sie nicht entwirrte C-Funktionen aus C ++ - Programmen heraus aufrufen können , sowie entwirrte C ++ - Funktionen aus C ++ - Programmen heraus , die andere Antworten nicht so offensichtlich machen, und 2) weil Sie unterschiedliche Beispiele dafür zeigen jeder. Vielen Dank!In jedem C ++ - Programm werden alle nicht statischen Funktionen in der Binärdatei als Symbole dargestellt. Diese Symbole sind spezielle Textzeichenfolgen, die eine Funktion im Programm eindeutig identifizieren.
In C entspricht der Symbolname dem Funktionsnamen. Dies ist möglich, weil in C keine zwei nicht statischen Funktionen denselben Namen haben können.
Da C ++ eine Überladung zulässt und viele Funktionen aufweist, die C nicht bietet - wie Klassen, Elementfunktionen, Ausnahmespezifikationen -, kann der Funktionsname nicht einfach als Symbolname verwendet werden. Um dies zu lösen, verwendet C ++ das sogenannte Name Mangling, das den Funktionsnamen und alle erforderlichen Informationen (wie die Anzahl und Größe der Argumente) in eine seltsam aussehende Zeichenfolge umwandelt, die nur vom Compiler und Linker verarbeitet wird.
Wenn Sie also eine Funktion als extern C angeben, führt der Compiler keine Namensverwaltung durch und kann direkt auf sie zugreifen, indem sein Symbolname als Funktionsname verwendet wird.
Dies ist praktisch, wenn Sie solche Funktionen verwenden
dlsym()
unddlopen()
aufrufen.quelle
C ++ verwirrt Funktionsnamen, um eine objektorientierte Sprache aus einer prozeduralen Sprache zu erstellen
Die meisten Programmiersprachen bauen nicht auf vorhandenen Programmiersprachen auf. C ++ baut auf C auf und ist darüber hinaus eine objektorientierte Programmiersprache, die aus einer prozeduralen Programmiersprache aufgebaut ist. Aus diesem Grund gibt es C ++ - Ausdrücke,
extern "C"
die Abwärtskompatibilität mit C bieten.Schauen wir uns das folgende Beispiel an:
Der AC-Compiler kompiliert das obige Beispiel nicht, da dieselbe Funktion
printMe
zweimal definiert wird (obwohl sie unterschiedliche Parameterint a
vs habenchar a
).Ein C ++ - Compiler kompiliert das obige Beispiel. Es ist egal, dass
printMe
zweimal definiert wird.Dies liegt daran, dass ein C ++ - Compiler Funktionen implizit basierend auf ihren Parametern umbenennt ( mangelt ). In C wurde diese Funktion nicht unterstützt. Wenn C ++ jedoch über C erstellt wurde, war die Sprache objektorientiert konzipiert und musste die Fähigkeit unterstützen, verschiedene Klassen mit gleichnamigen Methoden (Funktionen) zu erstellen und Methoden ( Methodenüberschreibung ) basierend auf verschiedenen Methoden zu überschreiben Parameter.
extern "C"
sagt "C-Funktionsnamen nicht entstellen"Stellen Sie sich jedoch vor, wir haben eine Legacy-C-Datei mit dem Namen "parent.c", deren
include
Funktionsnamen aus anderen Legacy-C-Dateien "parent.h", "child.h" usw. stammen. Wenn die Legacy-Datei "parent.c" ausgeführt wird Durch einen C ++ - Compiler werden die Funktionsnamen entstellt und stimmen nicht mehr mit den in "parent.h", "child.h" usw. angegebenen Funktionsnamen überein. Daher müssten auch die Funktionsnamen in diesen externen Dateien verwendet werden verstümmelt werden. Das Mangeln von Funktionsnamen in einem komplexen C-Programm mit vielen Abhängigkeiten kann zu fehlerhaftem Code führen. Daher kann es zweckmäßig sein, ein Schlüsselwort anzugeben, das den C ++ - Compiler anweist, einen Funktionsnamen nicht zu entstellen.Das
extern "C"
Schlüsselwort weist einen C ++ - Compiler an, C-Funktionsnamen nicht zu entstellen (umzubenennen).Zum Beispiel:
extern "C" void printMe(int a);
quelle
extern "C"
wenn wir nur einedll
Datei haben? Ich meine, wenn wir keine Header-Datei haben und nur eine Quelldatei (nur Implementierungen) und die Verwendung ihrer Funktion über den Funktionszeiger. In diesem Zustand haben wir nur Funktionen verwendet (unabhängig von ihrem Namen).Kein C-Header kann mit C ++ kompatibel gemacht werden, indem lediglich externes "C" eingeschlossen wird. Wenn Bezeichner in einem C-Header mit C ++ - Schlüsselwörtern in Konflikt stehen, beschwert sich der C ++ - Compiler darüber.
Zum Beispiel habe ich gesehen, dass der folgende Code in einem g ++ fehlschlägt:
Ein bisschen macht Sinn, ist aber etwas zu beachten, wenn Sie C-Code nach C ++ portieren.
quelle
extern "C"
bedeutet, die C-Verknüpfung zu verwenden, wie in anderen Antworten beschrieben. Es bedeutet nicht, "den Inhalt als C zu kompilieren" oder so.int virtual;
ist in C ++ ungültig und die Angabe einer anderen Verknüpfung ändert daran nichts.Es ändert die Verknüpfung einer Funktion so, dass die Funktion von C aus aufgerufen werden kann. In der Praxis bedeutet dies, dass der Funktionsname nicht entstellt wird .
quelle
undname
.Es weist den C ++ - Compiler an, beim Verknüpfen die Namen dieser Funktionen in einem C-Stil nachzuschlagen, da die Namen der in C und C ++ kompilierten Funktionen während der Verknüpfungsphase unterschiedlich sind.
quelle
extern "C" soll von einem C ++ - Compiler erkannt werden und den Compiler darüber informieren, dass die angegebene Funktion im C-Stil kompiliert wird (oder werden soll). Während der Verknüpfung wird auf die korrekte Version der Funktion von C verwiesen.
quelle
Ich habe 'extern "C"' zuvor für DLL-Dateien (Dynamic Link Library) verwendet, um usw. die main () -Funktion "exportierbar" zu machen, damit sie später in einer anderen ausführbaren Datei von DLL verwendet werden kann. Vielleicht kann ein Beispiel dafür nützlich sein, wo ich es früher verwendet habe.
DLL
EXE
quelle
extern "C"
und__declspec(dllexport)
sind nicht verwandt. Ersteres steuert die Symboldekoration, letzteres ist für die Erstellung eines Exporteintrags verantwortlich. Sie können ein Symbol auch mit C ++ - Namensdekoration exportieren. Abgesehen davon, dass der Punkt dieser Frage völlig verfehlt wurde, gibt es auch andere Fehler im Codebeispiel. Zum einenmain
deklariert der Export aus Ihrer DLL keinen Rückgabewert. Oder Konvention nennen. Beim Importieren weisen Sie eine zufällige Aufrufkonvention zu (WINAPI
) und verwenden das falsche Symbol für 32-Bit-Builds (sollte_main
oder sein_main@0
). Entschuldigung, -1.void*
, aber Ihre Implementierung gibt nichts zurück. Das wird wirklich gut fliegen ...extern "C"
ist eine Verknüpfungsspezifikation, mit der C-Funktionen in den Cpp-Quelldateien aufgerufen werden . Wir können C-Funktionen aufrufen, Variablen schreiben und Header einschließen . Die Funktion wird in einer externen Entität deklariert und außerhalb definiert. Syntax istTyp 1:
Typ 2:
z.B:
quelle
Diese Antwort ist für die ungeduldigen / zu befolgenden Fristen, nur ein Teil / eine einfache Erklärung ist unten:
Also
in C ++, mit dem Namen Mangeln eindeutig Identitäten jede Funktion
in C, auch ohne Namen Mangeln eindeutig Identitäten jede Funktion
Um das Verhalten von C zu ändern ++, das heißt, zu spezifizieren , dass Namen Mangeln nicht sollte für eine bestimmte Funktion geschehen, können Sie extern „C“ vor dem Funktionsnamen, für welche Gründe auch immer, wie eine Funktion mit einem bestimmten Namen aus einer DLL exportieren , zur Verwendung durch seine Kunden.
Lesen Sie andere Antworten, um detailliertere / korrektere Antworten zu erhalten.
quelle
Beim Mischen von C und C ++ (dh a. Aufrufen der C-Funktion von C ++ und b. Aufrufen der C ++ - Funktion von C) verursacht das Mangeln von C ++ - Namen Verknüpfungsprobleme. Technisch gesehen tritt dieses Problem nur auf, wenn die Angerufenenfunktionen bereits mit dem entsprechenden Compiler in Binärdateien (höchstwahrscheinlich eine * .a-Bibliotheksdatei) kompiliert wurden.
Wir müssen also externes "C" verwenden, um das Mangeln von Namen in C ++ zu deaktivieren.
quelle
Ohne mit anderen guten Antworten in Konflikt zu geraten, werde ich ein wenig von meinem Beispiel hinzufügen.
Was genau der C ++ - Compiler tut: Er entstellt die Namen im Kompilierungsprozess, daher müssen wir den Compiler anweisen, die
C
Implementierung speziell zu behandeln .Wenn wir C ++ - Klassen erstellen und hinzufügen
extern "C"
, teilen wir unserem C ++ - Compiler mit, dass wir die C-Aufrufkonvention verwenden.Grund (wir rufen die C-Implementierung aus C ++ auf): Entweder möchten wir die C-Funktion aus C ++ aufrufen oder die C ++ - Funktion aus C aufrufen (C ++ - Klassen ... usw. funktionieren in C nicht).
quelle
Eine von einem C-Compiler kompilierte Funktion void f () und eine von einem C ++ - Compiler kompilierte Funktion void f () sind nicht dieselbe Funktion. Wenn Sie diese Funktion in C geschrieben und dann versucht haben, sie von C ++ aus aufzurufen, sucht der Linker nach der C ++ - Funktion und findet die C-Funktion nicht.
extern "C" teilt dem C ++ - Compiler mit, dass Sie eine Funktion haben, die vom C-Compiler kompiliert wurde. Sobald Sie ihm mitteilen, dass es vom C-Compiler kompiliert wurde, weiß der C ++ - Compiler, wie er es korrekt aufruft.
Außerdem kann der C ++ - Compiler eine C ++ - Funktion so kompilieren, dass der C-Compiler sie aufrufen kann. Diese Funktion wäre offiziell eine C-Funktion, aber da sie vom C ++ - Compiler kompiliert wird, kann sie alle C ++ - Funktionen verwenden und verfügt über alle C ++ - Schlüsselwörter.
quelle
extern "C"
Funktion kompilieren - und (unter bestimmten Einschränkungen) kann sie durch Code aufgerufen werden, der von einem C-Compiler kompiliert wurde.