承接上篇文章,這篇用 C 呼叫 C++ 為例說明相關的觀念。
說明用的原始碼:
/* b.h */
#ifndef _B_H_
#define _B_H_#ifdef __cplusplus
extern "C" {
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif
#endif/* b.cpp */
#include "b.h"int add(int a, int b) {
return a + b;
}/* a.c */
#include <stdio.h>
#include "b.h"int main(void) {
printf("%d\n", add(3, 5));
return 0;
}
編譯和執行
$ g++ -c a.c
$ g++ -c b.cpp
$ g++ a.o b.o -o a
$ ./a # 輸出 8# 查看 symbol
$ nm b.o
0000000000000000 T add
$ nm a.o
U add
0000000000000000 T main
U printf
說明
《How to mix C and C++ Updated! , C++ FAQ》說明 C 和 C++ 互相呼叫的注意事項。其中在 C 呼叫 C++ 的函式時,要注意 C++ 有 name mangling,不管是 class method 還是 function,名稱 X 編出來都不會是 X,所以要用 extern “C”
避免 name mangling,這樣 C 的程式和 C++ 的程式都能認得它們用的 symbol。
細節說明如下:
- 寫 b.h 時用
#ifdef __cplusplus
讓 a.c 和 b.cpp 都能 include b.h,且看到不同的東西。可用g++ -E
查看前置處理器展開的結果。 - 編譯 b.cpp 時,透過 b.h 加入
extern “C”
,這樣就不會做 name mangling 。上例 add() 在有 name mangling 的情況,產生的 symbol 是_Z3addii
,沒有name mangling 則是add
。附帶一提,可用nm -C
直接輸出 demangle 的名稱,或用c++filt STRING
輸出 demangle 的名稱。 - 由於 C 沒有
extern “C”
的語法, b.h 裡面有這段的話會編譯錯誤要透過#ifdef
避開。 - 從 linker 的角度來看,a.o 用到 add,而 b.o 準備了 add,沒有問題,可以產生執行檔。
另一方面,若保留 name mangling,結果如下:
# 查看有 name mangling 的 symbol
$ g++ -U __cplusplus -c b.cpp
$ nm b.o
0000000000000000 T _Z3addii
$ nm -C b.o
0000000000000000 T add(int, int)
$ g++ a.o b.o -o a
a.o: In function `main':
a.c:(.text+0xf): undefined reference to `add'
collect2: error: ld returned 1 exit status
- 用
g++ -U __cplusplus
取消#define __cpluscplus
, 所以add
的宣告沒有包在extern “C”
內。編譯函式add
後產生的 symbol 有 mangling。 - 結果 linker 發現 a.o 要呼叫
add
,但 b.o 內是_Z3addii
,無法滿足 a.o 的要求,產生 linking error。