Welche Auswirkung hat externes „C“ in C++?
-
22-07-2019 - |
Frage
Was genau bedeutet Putten? extern "C"
in C++-Code tun?
Zum Beispiel:
extern "C" {
void foo();
}
Lösung
extern „C“ stellt ein Funktionsname in C ++, C 'Verknüpfung hat (Compiler mangle nicht den Namen), so dass Client-C-Code verlinkt auf (dh Nutzung) Ihre Funktion eine ‚C‘ kompatible Header-Datei verwenden, enthält nur die Deklaration Ihrer Funktion. Ihre Funktionsdefinition in einem binären Format enthalten ist (das von Ihrer C ++ Compiler kompiliert wurde), dass der Client ‚C‘ Linker wird dann verbindet die ‚C‘ Namen zu verwenden.
Da C ++ hat von Funktionsnamen Überlastung und C nicht, die C ++ Compiler kann nicht nur die Funktionsnamen als eindeutige ID verwenden, um zu verknüpfen, so dass er den Namen verstümmelt, indem sie Informationen über die Argumente hinzufügen. AC-Compiler muss nicht um den Namen zerfleischen, da Sie nicht Funktionsnamen in C überlasten können, wenn Sie feststellen, dass eine Funktion extern „C“ Verknüpfung in C ++ hat, der C ++ Compiler nicht Argument / Parameter Typinformation auf den Namen für fügt verwendet Verknüpfung.
Nur damit Sie wissen, können Sie „C“ Verknüpfung auf jede einzelne Erklärung / Definition explizit angeben oder einen Block Gruppe eine gewisse Bindung an eine Folge von Deklarationen / Definitionen verwenden:
extern "C" void foo(int);
extern "C"
{
void g(char);
int i;
}
Wenn Sie über die technischen Details kümmern, werden sie in Abschnitt 7.5 des C ++ 03-Standard, hier eine kurze Zusammenfassung (mit Schwerpunkt auf extern „C“):
- extern "C" ist eine Verknüpfung-Spezifikation
- Jeder Compiler ist erforderlich , um "C" Verknüpfung
- eine Verknüpfung Spezifikation wird nur im Namensraum Umfang auftreten
-
alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprache Verknüpfungsehen Sie Richards Kommentar: Nur Funktionsnamen und Variablennamen mit externer Bindung haben eine Sprache Verknüpfung - zwei Funktionstypen mit unterschiedlichen Sprachbindungen sind unterschiedliche Typen, auch wenn ansonsten identisch
- Verknüpfung specs Nest, innere bestimmt die letzte Verknüpfung
- extern "C" ist für die Teilnehmer ignoriert
- höchstens eine Funktion mit einem bestimmten Namen kann „C“ Verknüpfung hat (unabhängig vom Namensraum)
-
extern "C" eine Funktion zwingt externe Bindung zu haben (kann nicht machen es statisch)sehen Sie Richard Kommentar: 'static' innen 'extern "C"' gültig ist; ein Unternehmen so hat interne Bindung erklärt, und so hat keine Sprache Verknüpfung - Verknüpfung von C ++ auf Objekte in anderen Sprachen definiert und Objekte in C ++ definierten aus anderen Sprachen ist die Implementierung definiert und sprachabhängig. Nur dort, wo die Objekt Layout Strategien von zwei Sprachimplementierungen ähnlich genug sind, um eine solche Verknüpfung erreicht werden kann
Andere Tipps
Ich wollte nur ein wenig Informationen hinzuzufügen, da ich nicht gesehen habe es geschrieben noch.
Sie werden sehr oft sehen Code in C-Header wie folgt:
#ifdef __cplusplus
extern "C" {
#endif
// all of your legacy C code here
#ifdef __cplusplus
}
#endif
Was dies erreicht ist, dass es Sie, dass die C-Header-Datei mit Ihrem C ++ Code zu verwenden, ermöglicht es, weil das Makro „__cplusplus“ definiert. Aber Sie können auch noch verwenden Sie es mit Ihrem alten C-Code, wo das Makro nicht definiert ist, so wird es nicht sehen die eindeutig C ++ konstruieren.
Obwohl, ich habe auch C ++ Code zu sehen, wie:
extern "C" {
#include "legacy_C_header.h"
}
was ich mir vorstellen, erreicht viel das gleiche.
Nicht sicher, welcher Weg ist besser, aber ich habe beide gesehen.
In jedem C ++ Programm, alle nicht-statischen Funktionen sind in der Binärdatei als Symbole dargestellt. Diese Symbole sind spezielle Textzeichenfolgen, die eindeutig eine Funktion im Programm identifizieren.
In C, der Symbolname ist der gleiche wie der Funktionsname. Dies ist möglich, weil in C keine zwei nicht-statischen Funktionen die gleichen Namen haben.
Da C ++ ermöglicht eine Überlastung und verfügt über viele Funktionen, die C nicht - wie Klassen, Member-Funktionen, Ausnahme Spezifikationen - es ist nicht möglich, einfach den Namen der Funktion als Symbolnamen zu verwenden. Zu lösen, dass C ++ verwendet sog Namen Mangeln, die den Namen der Funktion verwandelt und alle notwendigen Informationen (wie die Anzahl und Größe der Argumente) in einige seltsame aussehende Zeichenfolge verarbeitet nur durch den Compiler und Linker.
Wenn Sie also eine Funktion angeben extern C zu sein, wird der Compiler nicht Namen mit ihm Mangeln führt und es kann direkt mit seinem Symbolnamen als Funktionsnamen aufgerufen.
Das ist praktisch bei der Verwendung von dlsym()
und dlopen()
für den Aufruf solcher Funktionen.
Decompile ein g++
erzeugten binären 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 mit GCC 4.8 Linux ELF Ausgabe:
g++ -c main.cpp
Decompile der Symboltabelle:
readelf -s main.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
Interpretation
Wir sehen, dass:
-
ef
undeg
wurden in Symbolen mit dem gleichen Namen wie in dem Code gespeichert
-
die anderen Symbole wurden verstümmelt. Lassen Sie uns unmangle sie:
$ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g()
Fazit: die beiden folgenden Symboltypen waren nicht verstümmelten:
- definiert
- deklariert, aber nicht definiert (
Ndx = UND
), an der Verbindung oder zur Laufzeit von einem anderen Objekt-Datei zur Verfügung gestellt werden
Sie werden also extern "C"
müssen sowohl beim Aufruf:
- C von C ++: tell
g++
unmangled Symbole vongcc
produziert erwarten - C ++ von C: tell
g++
unmangled Symbole zu erzeugen, fürgcc
zu verwenden
Dinge, die in extern C nicht funktionieren
Es wird offensichtlich, dass jede C ++ Funktion, die Namen Mangeln erfordert innerhalb extern C
wird nicht funktionieren:
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) { }
}
Minimal runnable C von C ++ Beispiel
Aus Gründen der Vollständigkeit und die newbs gibt, siehe auch: Wie C-Quelldateien in einem C ++ Projekt verwenden?
Der Aufruf C von C ++ ist recht einfach:. Jede C-Funktion hat nur eine mögliche Nicht-zerfleischt Symbol, so dass keine zusätzliche Arbeit erforderlich ist,
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
C. H
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
C.C
#include "c.h"
int f(void) { return 1; }
Ausführen:
g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out
Ohne extern "C"
die Verbindung fehlschlägt mit:
main.cpp:6: undefined reference to `f()'
da g++
erwartet einen verstümmelten f
zu finden, die gcc
nicht produzieren.
Minimal runnable C ++ von C Beispiel
C ++ Aufruf von C etwas schwieriger ist. Wir müssen manuell erstellen Nicht-verstümmelten Versionen jeder Funktion, die wir machen möchten
Hier zeigen wir, wie C ++ belichten Funktion Überlastungen C.
main.c
#include <assert.h>
#include "cpp.h"
int main(void) {
assert(f_int(1) == 2);
assert(f_float(1.0) == 3);
return 0;
}
cpp.h
#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" {
#endif
int 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);
}
Ausführen:
gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out
Ohne extern "C"
es nicht mit:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
da g++
verstümmelten Symbole erzeugt, die gcc
nicht finden kann.
Getestet in Ubuntu 18.04.
C ++ Funktionsnamen mangles eine objektorientierte Sprache aus einer prozeduralen Sprache
erstellen Die meisten Programmiersprachen sind nicht on-top vorhandener Programmiersprachen gebaut. C ++ ist on-top von C gebaut, und außerdem ist es eine objektorientierte Programmiersprache von einer prozeduralen Programmiersprache gebaut, und aus diesem Grunde gibt es C ++ Ausdrücke wie extern "C"
die Kompatibilität bietet rückwärts mit C.
Die im folgende Beispiel aussehen lassen:
#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, da die gleiche Funktion printMe
zweimal definiert ist (auch wenn sie unterschiedliche Parameter int a
vs char a
haben).
gcc -o printMe printMe.c && ./printMe;
1 Fehler. PrintMe wird mehr als einmal definiert.
Ein C ++ Compiler wird das obige Beispiel kompilieren. Es ist egal, dass printMe
zweimal definiert ist.
g ++ -o printMe printMe.c && ./printMe;
Das ist, weil ein C ++ Compiler implizit umbenennt ( Mangeln ) Funktionen auf der Grundlage ihrer Parameter. In C wurde diese Funktion nicht unterstützt. Wenn jedoch C ++ über C gebaut wurde, wurde die Sprache entwickelt, um objektorientiert, und notwendig, um die Fähigkeit zur Unterstützung der verschiedenen Klassen mit Methoden (Funktionen) mit dem gleichen Namen zu erstellen und Methoden außer Kraft zu setzen ( Methode überschrieben ) auf der Basis verschiedener Parameter //en.wikipedia.org/wiki/Method_overriding
sagt extern "C"
"nicht C-Funktionsnamen mangle"
Allerdings stellen wir uns ein Vermächtnis C Datei mit dem Namen "parent.c" haben, dass include
s Funktionsnamen aus anderen Legacy-C-Dateien, "parent.h", "child.h" usw. Wenn das Erbe "parent.c" Datei wird durch einen C ++ Compiler laufen, dann die Funktionsnamen verstümmelt werden, und sie werden nicht mehr die Funktionsnamen in „parent.h“, „child.h“ usw. angegeben passen - so die Funktionsnamen in den externen Dateien würden müssen auch verstümmelt werden. Mangeln Funktionsnamen in einem komplexen C-Programm können diejenigen mit vielen Abhängigkeiten zu defektem Code führen; so könnte es zweckmäßig sein, ein Schlüsselwort zu schaffen, die den C ++ Compiler kann nicht sagen, einen Funktionsnamen mangle.
Das extern "C"
Schlüsselwort erzählt eine C ++ Compiler nicht mangle (umbenennen) C-Funktionsnamen. Beispiel Nutzung: extern "C" void printMe(int a);
Es ändert die Verknüpfung einer Funktion in einer solchen Art und Weise, dass die Funktion von C. In der Praxis aufrufbar ist, das bedeutet, dass der Name der Funktion nicht verstümmelten .
Nicht jeder C-Header kann mit C kompatibel gemacht werden ++, indem lediglich in extern "C" Verpackung. Wenn Bezeichner in einem C-Header-Konflikt mit C ++, um den C ++ Compiler Keywords werden darüber beschweren.
Zum Beispiel habe ich den folgenden Code gesehen nicht in einem g ++:
extern "C" {
struct method {
int virtual;
};
}
macht Kinda Sinn, sondern ist etwas im Auge zu behalten, wenn C-Code zu C ++ zu portieren.
Sie informiert den C ++ Compiler die Namen dieser Funktionen in einem C-Stil zu sehen, wenn die Verknüpfung, da die Namen von Funktionen kompilierten in C und C ++ während des Verknüpfungsstufe unterschiedlich sind.
extern „C“ bedeutet, die von einem C ++ Compiler erkannt zu werden und den Compiler, dass die bekannte Funktion ist (oder zu sein) erstellt in C-Stil zu benachrichtigen. So dass, während der Verknüpfung, um es in der richtigen Version der Funktion von C zu verbinden.
Ich benutze ‚extern‚C‘‘ vor für DLL (Dynamic Link Library) -Dateien machen usw. main () Funktion „exportierbar“, so dass es später in einer anderen ausführbaren Datei von DLL verwendet werden kann. Vielleicht ein Beispiel wo ich es verwenden kann 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 Verknüpfung Spezifikation, die auf Aufruf C-Funktionen verwendet wird, in den Cpp Quelldateien . Wir können Aufruf C-Funktionen, schreiben Variablen & schließen Header . Die Funktion wird in externen Entität deklariert & es ist außerhalb definiert. Syntax ist
Typ 1:
extern "language" function-prototype
Typ 2:
extern "language"
{
function-prototype
};
Beispiel:
#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)
{
//
//
}
Diese Antwort richtet sich an Ungeduldige bzw. diejenigen, die Fristen einhalten müssen. Nachfolgend finden Sie nur einen Teil bzw. eine einfache Erklärung:
- In C++ können Sie durch Überladen denselben Namen in der Klasse haben (da sie beispielsweise alle denselben Namen haben, können sie nicht unverändert aus der DLL usw. exportiert werden). Die Lösung für diese Probleme besteht darin, dass sie in unterschiedliche Zeichenfolgen (sogenannte Symbole) konvertiert werden ), Symbole berücksichtigen den Namen der Funktion und die Argumente, sodass jede dieser Funktionen, auch mit demselben Namen, eindeutig identifiziert werden kann (auch Namensverfälschung genannt).
- In C gibt es keine Überladung, der Funktionsname ist eindeutig (daher ist keine separate Zeichenfolge zur eindeutigen Identifizierung eines Funktionsnamens erforderlich, das Symbol ist also der Funktionsname selbst).
Also
in C++, wobei die Namensverzerrung jede Funktion eindeutig identifiziert
In C identifiziert jede Funktion auch ohne Namensverzerrung eindeutig
Um das Verhalten von C++ zu ändern, d. h. um die Namensverzerrung anzugeben sollte nicht Dies geschieht für eine bestimmte Funktion, die Sie verwenden können externes „C“ vor dem Funktionsnamen, aus welchem Grund auch immer, z. B. beim Exportieren einer Funktion mit einem bestimmten Namen aus einer DLL zur Verwendung durch ihre Clients.
Lesen Sie andere Antworten, um detailliertere/richtigere Antworten zu erhalten.
Beim Mischen C und C ++ (das heißt, eine anruf C Funktion von C ++;.. Und b Aufruf C ++ Funktion von C), der C ++ Namen Ursachen Mangeln Probleme verknüpft. Technisch gesehen ist dieses Problem nur geschieht, wenn die Angerufene Funktionen bereits in binäre kompiliert wurden (am wahrscheinlichsten, a * .a Bibliotheksdatei), um den entsprechenden Compiler.
Wir brauchen also extern "C" verwenden, um den Namen in C Mangeln deaktivieren ++.