Was genau macht das Putten? extern "C"
in C ++ Code tun?
Beispielsweise:
extern "C" {
void foo();
}
Was genau macht das Putten? extern "C"
in C ++ Code tun?
Beispielsweise:
extern "C" {
void foo();
}
extern "C" bewirkt, dass ein Funktionsname in C ++ eine C-Verknüpfung hat (der Compiler mischt den Namen nicht), so dass Client-C-Code mit einer C-kompatiblen Header-Datei, die nur die Datei enthält, eine Verknüpfung herstellen kann Erklärung Ihrer Funktion. Ihre Funktionsdefinition ist in einem Binärformat enthalten (das von Ihrem C ++ - Compiler kompiliert wurde), das der Client-C-Linker dann mit dem C-Namen verknüpft.
Da C ++ die Funktionsnamen überladen hat und C nicht, kann der C ++ - Compiler den Funktionsnamen nicht einfach als eindeutige ID verwenden, um ihn zu verlinken. Daher wird der Name durch Hinzufügen von Informationen zu den Argumenten gemangelt. Der AC-Compiler muss den Namen nicht ändern, da Funktionsnamen in C nicht überladen werden können. Wenn Sie in C ++ angeben, dass eine Funktion eine externe C-Verknüpfung hat, fügt der C ++ - Compiler keine Argument- / Parametertypinformationen hinzu Verknüpfung.
Nur damit Sie wissen, können Sie explizit eine C-Verknüpfung zu jeder einzelnen Deklaration / Definition angeben oder einen Block verwenden, um eine Sequenz von Deklarationen / Definitionen so zu gruppieren, dass sie eine bestimmte Verknüpfung haben:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
Wenn Sie sich für die technischen Details interessieren, sind diese in Abschnitt 7.5 der C ++ 03 Norm aufgelistet, hier eine kurze Zusammenfassung (mit Schwerpunkt auf extern "C"):
Ich wollte nur ein paar Infos hinzufügen, da ich es noch nicht gepostet habe.
Sie werden sehr häufig Code in C-Headern sehen:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
Dadurch können Sie diese C-Headerdatei mit Ihrem C ++ - Code verwenden, da das Makro "__cplusplus" definiert wird. Doch kannst du ebenfalls benutze es immer noch mit deinem älteren C-Code, wo das Makro ist NICHT definiert, sodass das eindeutige C ++ - Konstrukt nicht angezeigt wird.
Obwohl, ich habe auch C ++ Code gesehen wie:
extern "C" {
#include "legacy_C_header.h"
}
Das, was ich mir vorstelle, bewirkt fast dasselbe.
Ich bin mir nicht sicher, welcher Weg besser ist, aber ich habe beides gesehen.
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 ist der Symbolname derselbe wie der Funktionsname. Dies ist möglich, weil in C keine zwei nicht statischen Funktionen den gleichen Namen haben können.
Da C ++ das Überladen erlaubt und viele Funktionen hat, die C nicht hat - wie Klassen, Elementfunktionen, Ausnahmenspezifikationen - ist es nicht möglich, einfach den Funktionsnamen als Symbolnamen zu verwenden. Um das zu lösen, verwendet C ++ den sogenannten name mangling, der den Funktionsnamen und alle notwendigen Informationen (wie die Anzahl und Größe der Argumente) in eine seltsame Zeichenfolge umwandelt, die nur vom Compiler und Linker verarbeitet wird.
Wenn Sie also eine Funktion angeben, die extern C sein soll, führt der Compiler keinen Namensmangel durch und kann direkt ausgeführt werden Zugriff über den Symbolnamen als Funktionsname.
Dies ist praktisch bei der Verwendung dlsym()
und dlopen()
um solche Funktionen aufzurufen.
Lasst uns dekompiliere die generierte Objektdatei g ++ um zu sehen, was in dieser Implementierung passiert.
Beispiel generieren
Eingang:
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Kompiliere mit GCC 4.8 Linux ELF-Ausgabe:
g++ -c a.cpp
Dekompiliere die Symboltabelle:
readelf -s a.o
Die Ausgabe enthält:
Num: Value Size Type Bind Vis Ndx Name
8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv
9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef
10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
Deutung
Wir sehen das:
ef
und eg
wurden in Symbolen mit dem gleichen Namen wie im Code gespeichert
die anderen Symbole wurden gemangelt. Lasst uns sie entwirren:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Fazit: Beide der folgenden Symboltypen waren nicht verstümmelt:
Ndx = UND
), die zur Laufzeit oder Laufzeit aus einer anderen Objektdatei bereitgestellt werdenDu wirst also brauchen extern "C"
beides beim Anrufen:
g++
unverhoffte Symbole zu erwarten, die von gcc
g++
unmigrierte Symbole für generieren gcc
benutzenDinge, die in extern nicht funktionieren C
Es wird offensichtlich, dass jede C ++ - Funktion, die einen Namensmangel erfordert, nicht innerhalb des Woks läuft extern C
:
extern "C" {
// Overloading.
// error: declaration of C function ‘void f(int)’ conflicts with
void f();
void f(int i);
// Templates.
// error: template with C linkage
template <class C> void f(C i) { }
}
Die meisten Programmiersprachen sind nicht auf vorhandenen Programmiersprachen aufgebaut. C ++ ist auf C aufgebaut, und außerdem ist es eine objektorientierte Programmiersprache, die aus einer prozeduralen Programmiersprache aufgebaut ist, und aus diesem Grund gibt es C ++ Schlüsselwörter wie extern
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 parameters
void printMe(int a) {
printf("int: %i\n", a);
}
void printMe(char a) {
printf("char: %c\n", a);
}
int main() {
printMe("a");
printMe(1);
return 0;
}
Ein C-Compiler wird das obige Beispiel nicht kompilieren, weil es dieselbe Funktion hat printMe
ist zweimal definiert (obwohl sie unterschiedliche Parameter haben int a
vs char a
).
gcc -o printMe printMe.c && ./printMe;
1 Fehler PrintMe ist mehr als einmal definiert.
Ein C ++ - Compiler wird das obige Beispiel kompilieren. Das interessiert es nicht printMe
ist zweimal definiert.
g ++ -o printMe printMe.c && ./printMe;
Dies liegt daran, dass ein C ++ - Compiler implizit umbenannt wird (Mangeln) Funktionen basierend auf ihren Parametern. In C wurde diese Funktion nicht unterstützt. Wenn C ++ jedoch über C erstellt wurde, wurde die Sprache objektorientiert entworfen und musste die Möglichkeit unterstützen, verschiedene Klassen mit gleichnamigen Methoden (Funktionen) zu erstellen und Methoden zu überschreiben (Überschreiben der Methode) basierend auf verschiedenen Parametern.
Stellen Sie sich jedoch vor, wir haben eine ältere C-Datei mit dem Namen "parent.c" include
s Funktionsnamen aus anderen Legacy-C-Dateien, "parent.h", "child.h" usw. Wenn die ältere "parent.c" -Datei über einen C ++ - Compiler läuft, werden die Funktionsnamen manipuliert, und sie werden stimmen nicht mehr mit den in "parent.h", "child.h" usw. angegebenen Funktionsnamen überein - daher müssten die Funktionsnamen in diesen externen Dateien ebenfalls gemangelt werden. Und das könnte ziemlich unordentlich werden. Es könnte also zweckmäßig sein, ein Schlüsselwort anzugeben, das dem C ++ - Compiler mitteilen kann, dass ein Funktionsname nicht fehlerhaft ist.
Das extern
Schlüsselwort weist einen C ++ - Compiler an, Funktionsnamen nicht zu verändern (umzubenennen). Beispielverwendung: extern void printMe(int a);
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 lautet verstümmelt.
Kein C-Header wird mit externem "C" kompiliert. Wenn Bezeichner in einem C-Header mit C ++ - Schlüsselwörtern in Konflikt stehen, wird sich der C ++ - Compiler darüber beschweren.
Zum Beispiel habe ich gesehen, dass der folgende Code in einem g ++ fehlschlägt:
extern "C" {
struct method {
int virtual;
};
}
Kinda macht Sinn, sollte aber bei der Portierung von C-Code nach C ++ beachtet werden.
Es informiert den C ++ - Compiler, die Namen dieser Funktionen beim Binden in einem C-Stil nachzuschlagen, da die Namen der in C und C ++ kompilierten Funktionen während der Verbindungsstufe unterschiedlich sind.
extern "C" soll von einem C ++ - Compiler erkannt werden und dem Compiler mitteilen, dass die angegebene Funktion in C-Stil kompiliert ist (oder zu sein hat). Damit verlinkt es zwar zur richtigen Version der Funktion von C.
Ich habe 'extern' C '' vorher für dll (dynamic link library) -Dateien verwendet, um die main () - Funktion 'exportierbar' zu machen, so dass sie später in einer anderen ausführbaren Datei von dll verwendet werden kann. Vielleicht kann ein Beispiel dafür, wo ich es benutzt habe, nützlich sein.
DLL
#include <string.h>
#include <windows.h>
using namespace 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>
using namespace std;
typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll
Function mainDLLFunc;//make a variable for function placeholder
int main()
{
char winDir[MAX_PATH];//will hold path of above dll
GetCurrentDirectory(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 dll
if(DLL==NULL)
{
FreeLibrary((HMODULE)DLL);//if load fails exit
return 0;
}
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 name
if(mainDLLFunc==NULL)
{
FreeLibrary((HMODULE)DLL);//if it fails exit
return 0;
}
mainDLLFunc();//run exported function
FreeLibrary((HMODULE)DLL);
}
extern "C"
ist eine Verbindungsspezifikation, die verwendet wird C-Funktionen aufrufen in dem Cpp-Quelldateien. Wir können Rufen Sie C-Funktionen auf, schreiben Sie Variablen und fügen Sie Header hinzu. Die Funktion ist in der externen Entität deklariert und sie ist außerhalb definiert. Syntax ist
Typ 1:
extern "language" function-prototype
Typ 2:
extern "language"
{
function-prototype
};
z.B:
#include<iostream>
using namespace std;
extern "C"
{
#include<stdio.h> // Include C Header
int n; // Declare a Variable
void func(int,int); // Declare a function (function prototype)
}
int main()
{
func(int a, int b); // Calling function . . .
return 0;
}
// Function definition . . .
void func(int m, int n)
{
//
//
}