C++ 語言的設計初衷是「a better C」,但這並不意味著 C++ 在處理全域變數與函數的編譯連結機制上與 C 語言完全一致。為了支援函數多載這一核心特性,C++ 採用了名稱修飾技術,這導致其符號命名規則與 C 語言截然不同。extern "C" 正是解決這一衝突、實現 C/C++ 混合編程的關鍵橋樑。
許多標準頭文件(如 VxWorks 的 vxWorks.h)都包含如下結構:
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/* ... 函數聲明 ... */
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
前後的 #ifndef/#define/#endif 是防止重複包含的標準衛語句。核心在於中間的 #ifdef __cplusplus 區塊:當編譯器識別為 C++ 環境時,將聲明包裹在 extern "C" 中,強制採用 C 風格的編譯連結規約。
extern 告訴編譯器:該函數或全域變數的定義在別處(或本模組稍後),允許跨模組引用。它只是聲明,不分配內存。對應關鍵字 static 則限制符號僅在本編譯單元內可見。只有需跨模組訪問的符號,才可能被 extern "C" 修飾。
這是核心所在。C++ 為支援多載,編譯器會將函數名、參數類型、個數編碼進符號名(即 Name Mangling,如 _foo_int_int)。C 語言無多載,符號僅為 _foo。
未加 extern "C" 時: C++ 編譯器生成修飾名,連結器尋找修飾名。
加上 extern "C" 後: 編譯器不再修飾名,連結器按原始名 _foo 查找。
// C++ 代碼引用 C 頭文件
extern "C" {
#include "c_library.h"
}
C++ 函數無法直接作為 C 回調,需編寫 extern "C" 包裝函數,內部再調用 C++ 邏輯。
為保證不同編譯器、語言調用一致性,動態庫公開 API 通常均聲明為 extern "C"。
this 指針,不符合 C 語言調用約定。extern "C" 聲明,定義處也必須加上,否則連結失敗。extern "C" 的本質是告訴 C++ 編譯器:「這些符號請按 C 語言規則處理」。它消除了名稱修飾帶來的符號不匹配,是 C/C++ 混合編程、動態庫導出、跨語言介面設計的基石。理解其背後的編譯連結機制,比死記語法更有價值。