Dekompilasi g++biner yang dihasilkan untuk melihat apa yang sedang terjadi
Untuk memahami mengapa externperlu, hal terbaik yang harus dilakukan adalah memahami apa yang terjadi secara detail di file objek dengan contoh:
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
void h() { g(); eg(); }
Kompilasi dengan keluaran ELF Linux GCC 4.8 :
g++ -c main.cpp
Dekompilasi tabel simbol:
readelf -s main.o
Outputnya berisi:
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
Penafsiran
Kami melihat bahwa:
efdan egdisimpan dalam simbol dengan nama yang sama seperti di kode
simbol-simbol lainnya hancur. Mari kita uraikan:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Kesimpulan: kedua tipe simbol berikut tidak rusak:
- ditentukan
- dideklarasikan tetapi tidak ditentukan (
Ndx = UND), untuk diberikan pada tautan atau waktu proses dari file objek lain
Jadi, Anda memerlukan extern "C"keduanya saat menelepon:
- C dari C ++: kirim
g++untuk mengharapkan simbol tak berubah yang diproduksi olehgcc
- C ++ dari C: kirim
g++untuk menghasilkan simbol tak beraturan untuk gccdigunakan
Hal-hal yang tidak bekerja di luar C
Jelas bahwa fitur C ++ apa pun yang membutuhkan name mangling tidak akan berfungsi di dalam extern C:
extern "C" {
void f();
void f(int i);
template <class C> void f(C i) { }
}
Minimal runnable C dari contoh C ++
Demi kelengkapan dan untuk newbs di luar sana, lihat juga: Bagaimana menggunakan file sumber C dalam proyek C ++?
Memanggil C dari C ++ cukup mudah: setiap fungsi C hanya memiliki satu kemungkinan simbol yang tidak rusak, jadi tidak diperlukan pekerjaan tambahan.
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
ch
#ifndef C_H
#define C_H
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
cc
#include "c.h"
int f(void) { return 1; }
Lari:
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
Tanpa extern "C"tautan gagal dengan:
main.cpp:6: undefined reference to `f()'
karena g++berharap menemukan yang hancur f, yang gcctidak menghasilkan.
Contoh di GitHub .
Minimal C ++ yang dapat dijalankan dari contoh C.
Memanggil C ++ dari agak lebih sulit: kita harus membuat versi non-rusak secara manual dari setiap fungsi yang ingin kita tampilkan.
Di sini kami mengilustrasikan cara mengekspos overload fungsi C ++ ke 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
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);
}
Lari:
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
Tanpanya extern "C"gagal dengan:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
karena g++menghasilkan simbol rusak yang gcctidak dapat ditemukan.
Contoh di GitHub .
Diuji di Ubuntu 18.04.