Dekompilasi g++
biner yang dihasilkan untuk melihat apa yang sedang terjadi
Untuk memahami mengapa extern
perlu, 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:
ef
dan eg
disimpan 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 gcc
digunakan
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 gcc
tidak 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 gcc
tidak dapat ditemukan.
Contoh di GitHub .
Diuji di Ubuntu 18.04.