Декомпилируйте g++
сгенерированный двоичный файл, чтобы увидеть, что происходит
Чтобы понять, зачем нужен extern
, лучше всего понять, что происходит в объектных файлах в деталях, на примере:
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Скомпилируйте с GCC 4.8 Linux Вывод в формате ELF:
g++ -c main.cpp
Декомпилируйте таблицу символов:
readelf -s main.o
Вывод содержит:
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
Устный перевод
Мы видим, что:
ef
и eg
хранились в символах с тем же именем, что и в коде
другие символы были искажены. Распутаем их:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Вывод: оба следующих типа символов не искажены:
- определенный
- объявлен, но не определен (
Ndx = UND
), должен быть предоставлен во время ссылки или во время выполнения из другого объектного файла
Таким образом, при звонке вам понадобятся extern "C"
оба:
- C из C ++: укажите
g++
, что следует ожидать несвязанных символов, созданных gcc
- C ++ из C: сообщить
g++
о создании несвязанных символов для gcc
, которые будут использоваться
Что не работает во внешнем C
Становится очевидным, что любая функция C ++, требующая изменения имени, не будет работать внутри 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) { }
}
Минимальный исполняемый C из примера C ++
Для полноты картины и для новичков см. Также: Как использовать исходные файлы C в проекте C ++?
Вызвать C из C ++ довольно просто: каждая функция C имеет только один возможный неискаженный символ, поэтому никаких дополнительных действий не требуется.
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; }
Бегать:
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
Без extern "C"
ссылка не работает:
main.cpp:6: undefined reference to `f()'
потому что g++
ожидает найти искалеченный f
, который gcc
не дал.
Пример на GitHub.
Минимальный исполняемый C ++ из примера C
Вызов C ++ из немного сложнее: мы должны вручную создавать неискаженные версии каждой функции, которую мы хотим предоставить.
Здесь мы проиллюстрируем, как открыть доступ к перегрузкам функций C ++ для 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);
}
Бегать:
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
Без extern "C"
он не работает с:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
потому что g++
генерирует искаженные символы, которые gcc
не могут найти.
Пример на GitHub.
Протестировано в Ubuntu 18.04.
person
Ciro Santilli 新疆再教育营六四事件ۍ
schedule
14.05.2019