Frage

Was genau bedeutet Putten? extern "C" in C++-Code tun?

Zum Beispiel:

extern "C" {
   void foo();
}
War es hilfreich?

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üpfung sehen 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 und eg 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 von gcc produziert erwarten
  • C ++ von C: tell g++ unmangled Symbole zu erzeugen, für gcc 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.

Beispiel auf GitHub .

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.

Beispiel auf GitHub .

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 includes 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 ++.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top