Frage Was ist der Effekt von extern "C" in C ++?


Was genau macht das Putten? extern "C" in C ++ Code tun?

Beispielsweise:

extern "C" {
   void foo();
}

1214
2018-06-25 02:10


Ursprung


Antworten:


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"):

  • extern "C" ist eine Verknüpfungsspezifikation
  • Jeder Compiler ist erforderlich um eine "C" -Verbindung bereitzustellen
  • 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" erzwingt, 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

1216
2018-06-25 02:12



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.


242
2017-10-21 01:08



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.


168
2018-06-25 05:22



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:

  • definiert
  • erklärt, aber undefiniert (Ndx = UND), die zur Laufzeit oder Laufzeit aus einer anderen Objektdatei bereitgestellt werden

Du wirst also brauchen extern "C" beides beim Anrufen:

  • C von C ++: erzähle g++ unverhoffte Symbole zu erwarten, die von gcc
  • C ++ von C: erzählen g++ unmigrierte Symbole für generieren gcc benutzen

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

120
2018-05-29 10:06



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

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.

Extern sagt "nicht funktionierende Funktionsnamen"

Stellen Sie sich jedoch vor, wir haben eine ältere C-Datei mit dem Namen "parent.c" includes 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);


23
2018-02-12 01:50



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.


22
2018-06-25 02:12



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.


21
2018-01-09 22:16



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.


16
2018-06-25 02:12



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.


13
2018-04-10 09:46



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

5
2017-07-01 15:43



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

3
2017-11-17 12:09