variable - visual c++ extern c




Was ist der Effekt von extern "C" in C++? (8)

C ++ mangelt Funktionsnamen, um eine objektorientierte Sprache aus einer prozeduralen Sprache zu erstellen

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;
}

Der AC-Compiler wird das obige Beispiel nicht kompilieren, da die gleiche Funktion printMe zweimal definiert ist (obwohl sie unterschiedliche Parameter 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. Es ist nicht printMe dass printMe zweimal definiert ist.

g ++ -o printMe printMe.c && ./printMe;

Dies liegt daran, dass ein C ++ - Compiler implizit Funktionen auf der Basis ihrer Parameter umbenennt ( en.wikipedia.org/wiki/Name_mangling ). 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 ( Methodenüberschreibung ) basierend auf anderen Methoden außer Kraft zu setzen Parameter.

Extern sagt "nicht funktionierende Funktionsnamen"

Stellen Sie sich jedoch vor, wir haben eine Legacy-C-Datei mit dem Namen "parent.c", include s Funktionsnamen aus anderen Legacy-C-Dateien enthält, "parent.h", "child.h" usw. Wenn die ältere "parent.c" -Datei ist Wenn Sie einen C ++ - Compiler durchlaufen, werden die Funktionsnamen manipuliert, und sie stimmen nicht mehr mit den in "parent.h", "child.h" usw. angegebenen Funktionsnamen überein - die Funktionsnamen in diesen externen Dateien müssten also übereinstimmen auch 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 Schlüsselwort extern teilt einem C ++ - Compiler mit, Funktionsnamen nicht zu verändern (umzubenennen). Beispielverwendung: extern void printMe(int a);

Was genau macht das extern "C" in C ++ - Code?

Beispielsweise:

extern "C" {
   void foo();
}

Beim Mischen von C und C ++ (dh a. Aufrufen von C-Funktion aus C ++ und b. Aufrufen von C ++ - Funktion aus C) verursacht die C ++ - Namensänderung Verknüpfungen Probleme. Technisch gesehen tritt dieses Problem nur dann auf, wenn die aufgerufenen Funktionen bereits mit dem entsprechenden Compiler in Binärdateien (höchstwahrscheinlich eine * .a-Bibliotheksdatei) kompiliert wurden.

Also müssen wir extern "C" verwenden, um den Namen Mangling in C ++ zu deaktivieren.


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);
}

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. Aber Sie können es auch weiterhin mit Ihrem Legacy-C-Code verwenden, wobei das Makro NICHT definiert ist, sodass es das eindeutige C ++ - Konstrukt nicht sehen 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.


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.


Lassen Sie uns die generierte Objektdatei g ++ dekompilieren, 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 wurden nicht verfälscht:

  • definiert
  • deklariert, aber undefiniert ( Ndx = UND ), zur Verknüpfung oder Laufzeit aus einer anderen Objektdatei bereitgestellt werden

Du brauchst also ein extern "C" wenn du extern "C" :

  • C von C ++: Sag g++ , dass von gcc erzeugte unmigrierte Symbole erwartet werden
  • C ++ von C: Sag g++ , um nicht gemammelte Symbole für gcc zu erzeugen

Dinge, die in extern nicht funktionieren C

Es wird offensichtlich, dass jede C ++ - Funktion, die eine Namensänderung erfordert, nicht innerhalb von extern C läuft:

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) { }
}

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 nicht einfach den Funktionsnamen als eindeutige ID verwenden, um ihn zu verlinken. Daher wird der Name durch Hinzufügen von Informationen über die Argumente 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"):

  • extern "C" ist eine Verknüpfungsspezifikation
  • Jeder Compiler muss eine C-Verknüpfung bereitstellen
  • Eine Verknüpfungsspezifikation darf nur im Namensraumbereich auftreten
  • Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung. Siehe Richards 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
  • linkage specs nest, das 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 Namensraum)
  • extern "C" bewirkt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen). Siehe Richards Kommentar: "statisch" innen "extern" C "" ist gültig; Eine deklarierte Entität hat eine interne Verknüpfung und daher keine Sprachverknüpfung
  • Die Verknüpfung von C ++ zu Objekten, die in anderen Sprachen definiert sind, und zu Objekten, die in C ++ aus anderen Sprachen definiert sind, ist implementierungsdefiniert und sprachabhängig. Nur wenn die Objektlayoutstrategien von zwei Sprachimplementierungen ähnlich genug sind, kann eine solche Verbindung erreicht werden

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.







extern-c