Biên dịch một g++
nhị phân được tạo để xem những gì đang xảy ra
Để hiểu tại sao extern
cần thiết, điều tốt nhất cần làm là hiểu chi tiết những gì đang diễn ra trong các tệp đối tượng với một ví dụ:
main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
Biên dịch với đầu ra ELF GCC 4.8 Linux :
g++ -c main.cpp
Dịch ngược bảng ký hiệu:
readelf -s main.o
Đầu ra chứa:
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
Diễn dịch
Chúng ta thấy rằng:
ef
và eg
được lưu trữ trong các ký hiệu có cùng tên như trong mã
các biểu tượng khác được đọc sai Chúng ta hãy tháo gỡ chúng:
$ c++filt _Z1fv
f()
$ c++filt _Z1hv
h()
$ c++filt _Z1gv
g()
Kết luận: cả hai loại ký hiệu sau không được đọc sai:
- xác định
- khai báo nhưng không xác định (
Ndx = UND
), được cung cấp tại liên kết hoặc thời gian chạy từ tệp đối tượng khác
Vì vậy, bạn sẽ cần extern "C"
cả hai khi gọi:
- C từ C ++: nói
g++
để mong đợi các biểu tượng không bị thay đổi được tạo bởigcc
- C ++ từ C: yêu
g++
cầu tạo các ký hiệu không thay đổi gcc
để sử dụng
Những thứ không hoạt động ở bên ngoài C
Rõ ràng là bất kỳ tính năng C ++ nào yêu cầu xáo trộn tên sẽ không hoạt động bên trong 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 runnable tối thiểu từ ví dụ C ++
Để hoàn thiện và cho các newbs ngoài kia, hãy xem thêm: Làm thế nào để sử dụng các tệp nguồn C trong một dự án C ++?
Gọi C từ C ++ khá dễ dàng: mỗi hàm C chỉ có một biểu tượng không bị sai lệch, do đó không cần phải làm thêm.
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; }
Chạy:
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
Không có extern "C"
liên kết thất bại với:
main.cpp:6: undefined reference to `f()'
bởi vì g++
hy vọng sẽ tìm thấy một máng xối f
, mà gcc
không sản xuất.
Ví dụ trên GitHub .
C ++ tối thiểu có thể chạy được từ ví dụ C
Gọi C ++ từ khó hơn một chút: chúng ta phải tự tạo các phiên bản không bị xáo trộn của từng chức năng mà chúng ta muốn phơi bày.
Ở đây chúng tôi minh họa cách phơi bày quá tải chức năng C ++ cho C.
C chính
#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);
}
Chạy:
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
Không có extern "C"
nó thất bại với:
main.c:6: undefined reference to `f_int'
main.c:7: undefined reference to `f_float'
bởi vì g++
tạo ra các biểu tượng mangled mà gcc
không thể tìm thấy.
Ví dụ trên GitHub .
Đã thử nghiệm trong Ubuntu 18.04.