| 語言 | 函數原型 | 符號表名稱 (示例) |
| C | `void foo(int, int);` | `_foo` |
| C++ | `void foo(int, int);` | `_foo_int_int` (編碼參數) |
| C++ | `void foo(int, float);` | `_foo_int_float` |
鏈接階段,C++ 目標文件尋找 `_foo_int_int`,C 庫提供 `_foo`,**符號不匹配 → `undefined reference`**。
extern "C" 解決方案
// 告訴 C++ 編譯器:用 C 規則生成符號 (不修飾)
extern "C" {
void foo(int x, int y); // 符號為 _foo
}
鏈接時能找到 C 庫導出的 `_foo`,完美對接。
標準頭文件雙語寫法 (foo.h)
#ifdef __cplusplus
extern "C" {
#endif
// 純 C 宣告,雙語通用
extern int foo(int x, int y);
#ifdef __cplusplus
}
#endif
- C 編譯器忽略 `extern "C"` 塊,正常處理。
- C++ 編譯器識別 `__cplusplus` 宏,啟用 C 鏈接規範。
完整構建演示
// foo.h (如上)
// foo.c
#include "foo.h"
int foo(int x, int y) { return x + y; }
// test.cpp
#include "foo.h"
#include
int main() { printf("Hello %d\n", foo(1,2)); }
// 編譯鏈接
gcc -c foo.c -o foo.o // C 編譯
g++ -c test.cpp -o test.o // C++ 編譯
g++ test.o foo.o -o test // C++ 鏈接器鏈接 .o
./test // Hello 3
進階場景
- C++ 實現 C 接口:`extern "C" void c_api() { /* C++ 代碼 */ }` 供 C 調用。
- 類成員函數:`extern "C"` **不能**修飾非靜態成員函數 (隱含 `this`),只能用於 `static` 成員或自由函數。
- 模板/重載:`extern "C"` 函數不可重載 (符號無修飾),同名會衝突。
- 動態庫 dlopen:`dlsym(handle, "foo")` 尋找 C 風格符號,配合 `extern "C"` 導出。
常見誤區
- 以為 `extern "C"` 改變調用約定 — **不變**,僅影響符號名;調用約定由 `__cdecl`/`__stdcall` 等控制。
- 在 `.cpp` 文件內部包含 C 頭文件時忘記 `extern "C"` — 導致符號修飾不匹配。
- 混淆 `extern` (作用域聲明) 與 `extern "C"` (鏈接規範) — 兩者可組合:`extern "C" int foo();` 同時聲明外部鏈接且 C 符號。
搞懂符號修飾與 `extern "C"`,C/C++ 混合編程不再踩坑!
- 上一篇文章: C語言判斷CPU大小端模式:三種經典方法與原理圖解
- 下一篇文章: C語言數據類型轉換陷阱:值轉換 vs 內存重解釋
Copyright ©
程式碼之家 All Rights Reserved