Dekompilieren Sie eine g++
generierte Binärdatei, um zu sehen, was los ist
Um zu verstehen, warum dies extern
erforderlich ist, müssen Sie am besten anhand eines Beispiels verstehen, was in den Objektdateien im Detail vor sich geht:
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 Sie mit der GCC 4.8 Linux ELF- Ausgabe:
g++ -c main.cpp
Dekompilieren Sie die 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
Deutung
Wir sehen das:
ef
und eg
wurden 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"
beides, 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 gcc
die 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 entstelltes 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++. */
#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 scheitert mit:
main.cpp:6: undefined reference to `f()'
weil g++
erwartet, eine verstümmelte zu finden f
, die gcc
nicht produziert hat.
Beispiel auf GitHub .
Minimal ausführbares C ++ aus C-Beispiel
Das Aufrufen von C ++ von ist etwas schwieriger: Wir müssen manuell nicht entstellte Versionen jeder Funktion erstellen, die wir verfügbar machen möchten.
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 gcc
nicht finden können.
Beispiel auf GitHub .
Getestet in Ubuntu 18.04.