Was bewirkt externes "C" in C ++?


1632

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

Zum Beispiel:

extern "C" {
   void foo();
}

82
Ich möchte Ihnen diesen Artikel vorstellen: http://www.agner.org/optimize/calling_conventions.pdf Hier erfahren Sie viel mehr über das Aufrufen von Konventionen und den Unterschied zwischen Compilern.
Sam Liao

1
@Litherum Auf den ersten Blick weist es den Compiler an, diesen Codebereich mit C zu kompilieren, vorausgesetzt, Sie haben einen Cross-Compiler. Dies bedeutet auch, dass Sie eine Cpp-Datei haben, in der Sie diese foo()Funktion haben.
ha9u63ar

1
@ ha9u63ar Es ist "aus dem Kopf". Der gesamte Rest Ihres Kommentars ist ebenfalls falsch. Ich empfehle Ihnen, es zu löschen.
TamaMcGlinn

Antworten:


1559

extern "C" bewirkt, dass ein Funktionsname in C ++ eine 'C'-Verknüpfung hat (der Compiler entstellt den Namen nicht), sodass der Client-C-Code mithilfe einer' C'-kompatiblen Header-Datei, die nur die enthält, eine Verknüpfung zu Ihrer Funktion herstellen (dh diese verwenden) kann Erklärung Ihrer Funktion. Ihre Funktionsdefinition ist in einem Binärformat enthalten (das von Ihrem C ++ - Compiler kompiliert wurde), das der Client-Linker "C" dann mit dem Namen "C" verknüpft.

Da in C ++ Funktionsnamen überladen sind und in C nicht, kann der C ++ - Compiler den Funktionsnamen nicht einfach als eindeutige ID zum Verknüpfen verwenden, sodass der Name durch Hinzufügen von Informationen zu den Argumenten entstellt wird. Der AC-Compiler muss den Namen nicht entstellen, da Sie Funktionsnamen in C nicht überladen können. Wenn Sie angeben, dass eine Funktion in C ++ über eine externe "C" -Verbindung verfügt, fügt der C ++ - Compiler dem verwendeten Namen keine Informationen zu Argumenten / Parametertypen hinzu Verknüpfung.

Damit Sie wissen, können Sie die Verknüpfung "C" zu jeder einzelnen Deklaration / Definition explizit angeben oder einen Block verwenden, um eine Folge von Deklarationen / Definitionen zu gruppieren, um eine bestimmte Verknüpfung zu erhalten:

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 des C ++ 03-Standards aufgeführt. Hier eine kurze Zusammenfassung (mit Schwerpunkt auf externem "C"):

  • extern "C" ist eine Verknüpfungsspezifikation
  • Jeder Compiler muss eine "C" -Verbindung bereitstellen
  • Eine Verknüpfungsspezifikation darf nur im Namespace-Bereich auftreten
  • Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung. Siehe Richard's 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
  • Verknüpfungsspezifikationen verschachteln, 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 Namespace)
  • extern "C" erzwingt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen). Siehe Richards Kommentar: 'static' inside 'extern "C"' ist gültig; Eine so deklarierte Entität verfügt über eine interne Verknüpfung und daher keine Sprachverknüpfung
  • Die Verknüpfung von C ++ mit in anderen Sprachen definierten Objekten und mit in C ++ definierten Objekten aus anderen Sprachen ist implementierungsdefiniert und sprachabhängig. Nur wenn die Objektlayoutstrategien von zwei Sprachimplementierungen ähnlich genug sind, kann eine solche Verknüpfung erreicht werden

22
Der C-Compiler verwendet kein Mangling, wie es C ++ tut. Wenn Sie also die AC-Schnittstelle von einem C ++ - Programm aus aufrufen möchten, müssen Sie eindeutig angeben, dass die C-Schnittstelle "extern c" ist.
Sam Liao

59
@Faisal: Versuchen Sie nicht, Code zu verknüpfen, der mit verschiedenen C ++ - Compilern erstellt wurde, auch wenn die Querverweise alle "extern" C "sind. Es gibt häufig Unterschiede zwischen den Layouts von Klassen oder den Mechanismen, die zur Behandlung von Ausnahmen verwendet werden, oder den Mechanismen, mit denen sichergestellt wird, dass Variablen vor der Verwendung initialisiert werden, oder anderen derartigen Unterschieden. Außerdem benötigen Sie möglicherweise zwei separate C ++ - Laufzeitunterstützungsbibliotheken (eine für jeder Compiler).
Jonathan Leffler

8
'extern "C" erzwingt, dass eine Funktion eine externe Verknüpfung hat (kann sie nicht statisch machen)' ist falsch. 'static' inside 'extern "C"' ist gültig; Eine so deklarierte Entität verfügt über eine interne Verknüpfung und daher keine Sprachverknüpfung.
Richard Smith

14
'Alle Funktionstypen, Funktionsnamen und Variablennamen haben eine Sprachverknüpfung' ist ebenfalls falsch. Nur Funktionsnamen und Variablennamen mit externer Verknüpfung haben eine Sprachverknüpfung.
Richard Smith

9
Beachten Sie, dass dies extern "C" { int i; }eine Definition ist. Dies ist möglicherweise nicht das, was Sie beabsichtigt haben, neben der Nichtdefinition von void g(char);. Um es zu einer Nichtdefinition zu machen, müssten Sie extern "C" { extern int i; }. Andererseits macht die Syntax mit einer Deklaration ohne geschweifte Klammern die Deklaration zu einer extern "C" int i;extern "C" { extern int i; }
Nichtdefinition

327

Ich wollte nur ein paar Informationen hinzufügen, da ich sie noch nicht gesehen habe.

Sie werden sehr oft Code in C-Headern sehen, wie folgt:

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Damit können Sie diese C-Header-Datei mit Ihrem C ++ - Code verwenden, da das Makro "__cplusplus" definiert wird. Sie können es aber auch weiterhin mit Ihrem alten C-Code verwenden, in dem das Makro NICHT definiert ist, sodass das eindeutige C ++ - Konstrukt nicht angezeigt wird.

Obwohl ich auch C ++ - Code gesehen habe wie:

extern "C" {
#include "legacy_C_header.h"
}

was ich mir vorstelle, bewirkt fast dasselbe.

Ich bin mir nicht sicher, welcher Weg besser ist, aber ich habe beide gesehen.


11
Es gibt einen deutlichen Unterschied. Im ersten Fall wird beim Kompilieren dieser Datei mit dem normalen gcc-Compiler ein Objekt generiert, bei dem der Funktionsname nicht entstellt wird. Wenn Sie dann C- und C ++ - Objekte mit dem Linker verknüpfen, werden die Funktionen NICHT gefunden. Sie müssen diese "Legacy-Header" -Dateien wie in Ihrem zweiten Codeblock mit dem Schlüsselwort extern einfügen.
Anne van Rossum

8
@Anne: Der C ++ - Compiler sucht auch nach nicht verwickelten Namen, da er extern "C"im Header angezeigt wird . Es funktioniert super, hat diese Technik oft benutzt.
Ben Voigt

20
@ Anne: Das stimmt nicht, der erste ist auch in Ordnung. Es wird vom C-Compiler ignoriert und hat den gleichen Effekt wie das zweite in C ++. Dem Compiler ist es egal, ob er extern "C"vor oder nach dem Einfügen des Headers auftritt. Bis es den Compiler erreicht, ist es sowieso nur ein langer Strom von vorverarbeitetem Text.
Ben Voigt

8
@ Anne, nein, ich denke, Sie wurden von einem anderen Fehler in der Quelle betroffen, weil das, was Sie beschreiben, falsch ist. Keine Version von g++hat dies in den letzten 17 Jahren für irgendein Ziel zu irgendeinem Zeitpunkt falsch verstanden. Der springende Punkt des ersten Beispiels ist, dass es keine Rolle spielt, ob Sie einen C- oder C ++ - Compiler verwenden. Für die Namen im extern "C"Block wird keine Namensverknüpfung durchgeführt .
Jonathan Wakely

7
"welches ist besser" - sicher ist die erste Variante besser: Sie ermöglicht es, den Header ohne weitere Anforderungen direkt in C- und C ++ - Code aufzunehmen. Der zweite Ansatz ist eine Problemumgehung für C-Header, bei denen der Autor die C ++ - Schutzvorrichtungen vergessen hat (kein Problem, wenn diese jedoch später hinzugefügt werden, werden verschachtelte externe "C" -Deklarationen akzeptiert ...).
Aconcagua

267

Dekompilieren Sie eine g++generierte Binärdatei, um 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 und disassemblieren Sie die generierte ELF- Ausgabe:

g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o

Die Ausgabe enthält:

     8: 0000000000000000     7 FUNC    GLOBAL DEFAULT    1 _Z1fv
     9: 0000000000000007     7 FUNC    GLOBAL DEFAULT    1 ef
    10: 000000000000000e    17 FUNC    GLOBAL DEFAULT    1 _Z1hv
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _Z1gv
    13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND eg

Deutung

Wir sehen das:

  • efund egwurden in Symbolen mit dem gleichen Namen wie im Code gespeichert

  • Die anderen Symbole wurden verstümmelt. Lassen Sie uns sie entwirren:

    $ c++filt _Z1fv
    f()
    $ c++filt _Z1hv
    h()
    $ c++filt _Z1gv
    g()

Schlussfolgerung: Beide der folgenden Symboltypen wurden nicht entstellt:

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

Sie benötigen also extern "C"beide, wenn Sie anrufen:

  • C aus C ++: Sagen Sie g++, dass Sie entwirrte Symbole erwarten sollen, die von erzeugt werdengcc
  • C ++ aus C: g++Weisen Sie an, nicht verschränkte Symbole für gccdie Verwendung zu generieren

Dinge, die in extern C nicht funktionieren

Es wird offensichtlich, dass jede C ++ - Funktion, die eine Namensveränderung erfordert, im Inneren nicht funktioniert 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) { }
}

Minimal ausführbares C aus C ++ - Beispiel

Der Vollständigkeit halber und für die Neulinge da draußen siehe auch: Wie verwende ich C-Quelldateien in einem C ++ - Projekt?

Das Aufrufen von C aus C ++ ist ziemlich einfach: Jede C-Funktion verfügt nur über ein mögliches nicht verstümmeltes Symbol, sodass keine zusätzliche Arbeit erforderlich ist.

main.cpp

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

CH

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++ 
 * because C does not know what this extern "C" thing is. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

cc

#include "c.h"

int f(void) { return 1; }

Lauf:

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"den Link schlägt fehl mit:

main.cpp:6: undefined reference to `f()'

denn g++erwartet, eine verstümmelte zu finden f, die gccnicht produziert hat.

Beispiel auf GitHub .

Minimal ausführbares C ++ aus C-Beispiel

Das Aufrufen von C ++ von C aus ist etwas schwieriger: Wir müssen nicht entstellte Versionen jeder Funktion, die wir verfügbar machen möchten, manuell erstellen.

Hier zeigen wir, wie C ++ - Funktionsüberladungen C ausgesetzt werden.

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

Lauf:

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"scheitert es mit:

main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'

weil g++erzeugte verstümmelte Symbole, die gccnicht finden können.

Beispiel auf GitHub .

Getestet in Ubuntu 18.04.


21
Beste Antwort, da Sie 1) ausdrücklich erwähnen, dass extern "C" {Sie nicht entwirrte C-Funktionen aus C ++ - Programmen heraus aufrufen können , sowie entwirrte C ++ - Funktionen aus C ++ - Programmen heraus , die andere Antworten nicht so offensichtlich machen, und 2) weil Sie unterschiedliche Beispiele dafür zeigen jeder. Vielen Dank!
Gabriel Staples

3
Ich mag diese Antwort sehr
Selbststart


1
@JaveneCPPMcGowan Warum denkst du, ich hätte einen C ++ - Lehrer? :-)
Ciro Santilli 1 病毒 审查 六四 事件 法轮功

205

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 entspricht der Symbolname dem Funktionsnamen. Dies ist möglich, weil in C keine zwei nicht statischen Funktionen denselben Namen haben können.

Da C ++ eine Überladung zulässt und viele Funktionen aufweist, die C nicht bietet - wie Klassen, Elementfunktionen, Ausnahmespezifikationen -, kann der Funktionsname nicht einfach als Symbolname verwendet werden. Um dies zu lösen, verwendet C ++ das sogenannte Name Mangling, das den Funktionsnamen und alle erforderlichen Informationen (wie die Anzahl und Größe der Argumente) in eine seltsam aussehende Zeichenfolge umwandelt, die nur vom Compiler und Linker verarbeitet wird.

Wenn Sie also eine Funktion als extern C angeben, führt der Compiler keine Namensverwaltung durch und kann direkt auf sie zugreifen, indem sein Symbolname als Funktionsname verwendet wird.

Dies ist praktisch, wenn Sie solche Funktionen verwenden dlsym()und dlopen()aufrufen.


Was meinst du mit praktisch? Ist Symbolname = Funktionsname würde den an dlsym übergebenen Symbolnamen bekannt machen oder etwas anderes?
Fehler

1
@ Fehler: ja. Im allgemeinen Fall ist es im Wesentlichen unmöglich, eine gemeinsam genutzte C ++ - Bibliothek mit nur einer Header-Datei zu öffnen () und die richtige Funktion zum Laden auszuwählen. (Auf x86 gibt es eine veröffentlichte Spezifikation zur Namensverfälschung in Form des Itanium ABI, die alle mir bekannten x86-Compiler zum Verfälschen von C ++ - Funktionsnamen verwenden, aber nichts in der Sprache erfordert dies.)
Jonathan Tomer

52

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

Die meisten Programmiersprachen bauen nicht auf vorhandenen Programmiersprachen auf. C ++ baut auf C auf und ist darüber hinaus eine objektorientierte Programmiersprache, die aus einer prozeduralen Programmiersprache aufgebaut ist. Aus diesem Grund gibt es C ++ - Ausdrücke, extern "C"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 kompiliert das obige Beispiel nicht, da dieselbe Funktion printMezweimal definiert wird (obwohl sie unterschiedliche Parameter int avs haben char a).

gcc -o printMe printMe.c && ./printMe;
1 Fehler. PrintMe wird mehrmals definiert.

Ein C ++ - Compiler kompiliert das obige Beispiel. Es ist egal, dass printMezweimal definiert wird.

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

Dies liegt daran, dass ein C ++ - Compiler Funktionen implizit basierend auf ihren Parametern umbenennt ( mangelt ). In C wurde diese Funktion nicht unterstützt. Wenn C ++ jedoch über C erstellt wurde, war die Sprache objektorientiert konzipiert und musste die Fähigkeit unterstützen, verschiedene Klassen mit gleichnamigen Methoden (Funktionen) zu erstellen und Methoden ( Methodenüberschreibung ) basierend auf verschiedenen Methoden zu überschreiben Parameter.

extern "C" sagt "C-Funktionsnamen nicht entstellen"

Stellen Sie sich jedoch vor, wir haben eine Legacy-C-Datei mit dem Namen "parent.c", deren includeFunktionsnamen aus anderen Legacy-C-Dateien "parent.h", "child.h" usw. stammen. Wenn die Legacy-Datei "parent.c" ausgeführt wird Durch einen C ++ - Compiler werden die Funktionsnamen entstellt und stimmen nicht mehr mit den in "parent.h", "child.h" usw. angegebenen Funktionsnamen überein. Daher müssten auch die Funktionsnamen in diesen externen Dateien verwendet werden verstümmelt werden. Das Mangeln von Funktionsnamen in einem komplexen C-Programm mit vielen Abhängigkeiten kann zu fehlerhaftem Code führen. Daher kann es zweckmäßig sein, ein Schlüsselwort anzugeben, das den C ++ - Compiler anweist, einen Funktionsnamen nicht zu entstellen.

Das extern "C"Schlüsselwort weist einen C ++ - Compiler an, C-Funktionsnamen nicht zu entstellen (umzubenennen).

Zum Beispiel:

extern "C" void printMe(int a);


können wir nicht verwenden, extern "C"wenn wir nur eine dllDatei haben? Ich meine, wenn wir keine Header-Datei haben und nur eine Quelldatei (nur Implementierungen) und die Verwendung ihrer Funktion über den Funktionszeiger. In diesem Zustand haben wir nur Funktionen verwendet (unabhängig von ihrem Namen).
BattleTested

@tfmontague, für mich hast du es richtig gemacht! gerade im Kopf.
Artanis Zeratul

29

Kein C-Header kann mit C ++ kompatibel gemacht werden, indem lediglich externes "C" eingeschlossen wird. Wenn Bezeichner in einem C-Header mit C ++ - Schlüsselwörtern in Konflikt stehen, beschwert sich der C ++ - Compiler darüber.

Zum Beispiel habe ich gesehen, dass der folgende Code in einem g ++ fehlschlägt:

extern "C" {
struct method {
    int virtual;
};
}

Ein bisschen macht Sinn, ist aber etwas zu beachten, wenn Sie C-Code nach C ++ portieren.


14
extern "C"bedeutet, die C-Verknüpfung zu verwenden, wie in anderen Antworten beschrieben. Es bedeutet nicht, "den Inhalt als C zu kompilieren" oder so. int virtual;ist in C ++ ungültig und die Angabe einer anderen Verknüpfung ändert daran nichts.
MM

1
... oder im Allgemeinen wird kein Code mit Syntaxfehlern kompiliert.
Valentin Heinitz

4
@ValentinHeinitz natürlich, obwohl die Verwendung von "virtual" als Bezeichner in C kein Syntaxfehler ist. Ich wollte nur darauf hinweisen, dass Sie in C ++ keinen C-Header automatisch verwenden können, indem Sie externes "C" darum setzen.
Sander Mertens

28

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 entstellt wird .


3
Verstümmelt ist der allgemein verwendete Begriff ... Glauben Sie nicht, dass ich jemals "dekoriert" mit dieser Bedeutung gesehen habe.
Matthew Scharley

1
Microsoft verwendet (zumindest teilweise) in seiner Dokumentation eher dekorierte als verstümmelte. Sie benennen sogar ihr Werkzeug, um einen Namen zu entdekorieren (auch bekannt als Entflechten) undname.
René Nyffenegger

20

Es weist den C ++ - Compiler an, beim Verknüpfen die Namen dieser Funktionen in einem C-Stil nachzuschlagen, da die Namen der in C und C ++ kompilierten Funktionen während der Verknüpfungsphase unterschiedlich sind.


12

extern "C" soll von einem C ++ - Compiler erkannt werden und den Compiler darüber informieren, dass die angegebene Funktion im C-Stil kompiliert wird (oder werden soll). Während der Verknüpfung wird auf die korrekte Version der Funktion von C verwiesen.


6

Ich habe 'extern "C"' zuvor für DLL-Dateien (Dynamic Link Library) verwendet, um usw. die main () -Funktion "exportierbar" zu machen, damit sie später in einer anderen ausführbaren Datei von DLL verwendet werden kann. Vielleicht kann ein Beispiel dafür nützlich sein, wo ich es früher verwendet habe.

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

4
Schwindel. extern "C"und __declspec(dllexport)sind nicht verwandt. Ersteres steuert die Symboldekoration, letzteres ist für die Erstellung eines Exporteintrags verantwortlich. Sie können ein Symbol auch mit C ++ - Namensdekoration exportieren. Abgesehen davon, dass der Punkt dieser Frage völlig verfehlt wurde, gibt es auch andere Fehler im Codebeispiel. Zum einen maindeklariert der Export aus Ihrer DLL keinen Rückgabewert. Oder Konvention nennen. Beim Importieren weisen Sie eine zufällige Aufrufkonvention zu ( WINAPI) und verwenden das falsche Symbol für 32-Bit-Builds (sollte _mainoder sein _main@0). Entschuldigung, -1.
Unsichtbarer

1
Das wiederholte sich nur, dass Sie nicht wissen, was Sie tun, aber es auf diese Weise zu tun, scheint für Sie zu funktionieren, für eine unbekannte Liste von Zielplattformen. Sie haben die Probleme, die ich in meinem vorherigen Kommentar angesprochen habe, nicht angesprochen. Dies ist immer noch eine Abwertung, da es völlig falsch ist (es gibt noch mehr, die nicht in einen einzigen Kommentar passen).
Unsichtbar

1
Das Posten einer Antwort auf Stack Overflow impliziert, dass Sie wissen, was Sie tun. Dies wird erwartet. Was Ihren Versuch betrifft, "eine Stapelbeschädigung beim Ausführen zu verhindern" : Ihre Funktionssignatur gibt einen Rückgabewert vom Typ an void*, aber Ihre Implementierung gibt nichts zurück. Das wird wirklich gut fliegen ...
Unsichtbar

1
Wenn Sie etwas implementieren, das durch reines Glück zu funktionieren scheint , dann wissen Sie eindeutig nicht , was Sie tun (Ihre "funktionierende" Stichprobe fällt in diese Kategorie). Es ist undefiniertes Verhalten, und es scheint eine gültige Form von undefiniertem Verhalten zu sein. Es ist immer noch undefiniert. Ich würde es sehr begrüßen, wenn Sie in Zukunft mehr Sorgfalt walten lassen würden. Ein Teil davon könnte darin bestehen, diese vorgeschlagene Antwort zu löschen.
Unsichtbar

1
Sie interpretieren eine Funktion, die nichts zurückgibt, als eine Funktion, die einen Zeiger zurückgibt. Es ist reines Glück, dass x86 in Bezug auf nicht übereinstimmende Funktionssignaturen und insbesondere Rückgabewerte vom integralen Typ sehr nachsichtig ist. Ihr Code funktioniert nur zufällig. Wenn Sie nicht einverstanden sind, müssen Sie erklären, warum Ihr Code zuverlässig funktioniert.
Unsichtbar

5

extern "C"ist eine Verknüpfungsspezifikation, mit der C-Funktionen in den Cpp-Quelldateien aufgerufen werden . Wir können C-Funktionen aufrufen, Variablen schreiben und Header einschließen . Die Funktion wird in einer externen Entität deklariert und 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

Diese Antwort ist für die ungeduldigen / zu befolgenden Fristen, nur ein Teil / eine einfache Erklärung ist unten:

  • In C ++ können Sie denselben Namen in der Klasse durch Überladen haben (zum Beispiel, da alle denselben Namen nicht wie besehen aus der DLL exportiert werden können usw.). Die Lösung für diese Probleme besteht darin, dass sie in verschiedene Zeichenfolgen (sogenannte Symbole) konvertiert werden ), Symbole berücksichtigen den Namen der Funktion, auch die Argumente, so dass jede dieser Funktionen auch mit demselben Namen eindeutig identifiziert werden kann (auch als Name Mangling bezeichnet).
  • In C gibt es keine Überladung. Der Funktionsname ist eindeutig (daher ist keine separate Zeichenfolge zum eindeutigen Identifizieren des Funktionsnamens erforderlich, daher ist das Symbol der Funktionsname selbst.)

Also
in C ++, mit dem Namen Mangeln eindeutig Identitäten jede Funktion
in C, auch ohne Namen Mangeln eindeutig Identitäten jede Funktion

Um das Verhalten von C zu ändern ++, das heißt, zu spezifizieren , dass Namen Mangeln nicht sollte für eine bestimmte Funktion geschehen, können Sie extern „C“ vor dem Funktionsnamen, für welche Gründe auch immer, wie eine Funktion mit einem bestimmten Namen aus einer DLL exportieren , zur Verwendung durch seine Kunden.

Lesen Sie andere Antworten, um detailliertere / korrektere Antworten zu erhalten.


1

Beim Mischen von C und C ++ (dh a. Aufrufen der C-Funktion von C ++ und b. Aufrufen der C ++ - Funktion von C) verursacht das Mangeln von C ++ - Namen Verknüpfungsprobleme. Technisch gesehen tritt dieses Problem nur auf, wenn die Angerufenenfunktionen bereits mit dem entsprechenden Compiler in Binärdateien (höchstwahrscheinlich eine * .a-Bibliotheksdatei) kompiliert wurden.

Wir müssen also externes "C" verwenden, um das Mangeln von Namen in C ++ zu deaktivieren.


0

Ohne mit anderen guten Antworten in Konflikt zu geraten, werde ich ein wenig von meinem Beispiel hinzufügen.

Was genau der C ++ - Compiler tut: Er entstellt die Namen im Kompilierungsprozess, daher müssen wir den Compiler anweisen, die C Implementierung speziell zu behandeln .

Wenn wir C ++ - Klassen erstellen und hinzufügen extern "C", teilen wir unserem C ++ - Compiler mit, dass wir die C-Aufrufkonvention verwenden.

Grund (wir rufen die C-Implementierung aus C ++ auf): Entweder möchten wir die C-Funktion aus C ++ aufrufen oder die C ++ - Funktion aus C aufrufen (C ++ - Klassen ... usw. funktionieren in C nicht).


Willkommen bei Stack Overflow. Wenn Sie sich entscheiden, eine ältere Frage zu beantworten, die gut etablierte und korrekte Antworten enthält, erhalten Sie möglicherweise keine Gutschrift, wenn Sie spät am Tag eine neue Antwort hinzufügen. Wenn Sie einige unverwechselbare neue Informationen haben oder davon überzeugt sind, dass die anderen Antworten alle falsch sind, fügen Sie auf jeden Fall eine neue Antwort hinzu, aber "noch eine Antwort", die die gleichen grundlegenden Informationen lange nach dem Stellen der Frage enthält, ist normalerweise gewonnen. " Ich verdiene dir nicht viel Kredit. Ehrlich gesagt glaube ich nicht, dass diese Antwort etwas Neues enthält.
Jonathan Leffler

Nun, ich hätte mich an deinen Punkt erinnern sollen - okay
Susobhan Das

-1

Eine von einem C-Compiler kompilierte Funktion void f () und eine von einem C ++ - Compiler kompilierte Funktion void f () sind nicht dieselbe Funktion. Wenn Sie diese Funktion in C geschrieben und dann versucht haben, sie von C ++ aus aufzurufen, sucht der Linker nach der C ++ - Funktion und findet die C-Funktion nicht.

extern "C" teilt dem C ++ - Compiler mit, dass Sie eine Funktion haben, die vom C-Compiler kompiliert wurde. Sobald Sie ihm mitteilen, dass es vom C-Compiler kompiliert wurde, weiß der C ++ - Compiler, wie er es korrekt aufruft.

Außerdem kann der C ++ - Compiler eine C ++ - Funktion so kompilieren, dass der C-Compiler sie aufrufen kann. Diese Funktion wäre offiziell eine C-Funktion, aber da sie vom C ++ - Compiler kompiliert wird, kann sie alle C ++ - Funktionen verwenden und verfügt über alle C ++ - Schlüsselwörter.


Der C ++ - Compiler kann eine extern "C"Funktion kompilieren - und (unter bestimmten Einschränkungen) kann sie durch Code aufgerufen werden, der von einem C-Compiler kompiliert wurde.
Jonathan Leffler
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.