在跨語言專案開發中,C 與 C++ 的互操作是常見且關鍵的課題。由於 C++ 引入了名稱修飾機制以支援函數多載,直接呼叫 C 函數或被 C 程式碼呼叫時,若不加適當處理,連結器將找不到正確的符號。本文系統整理兩大核心場景的解決方案,並提供可直接套用的標準模式。
當以 C 語言編寫 DLL 並需提供給 C++ 模組呼叫時,關鍵在於防止 C++ 編譯器對函數名進行修飾。標準做法是利用 __cplusplus 預處理器宏,在標頭檔中以 extern "C" 包裹導出宣告:
// MyCFuncs.h
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllimport) void MyCFunc();
__declspec(dllimport) void AnotherCFunc();
#ifdef __cplusplus
}
#endif
此模式確保:
extern "C",正常編譯;#include,零修改復用。若現有 C 標頭檔未採用上述技巧,而在 C++ 原始檔中需引用它,可在 #include 時以區塊包裹:
extern "C" {
#include "MyCHeader.h"
}
這樣即可強制編譯器將標頭檔內所有函數視為 C 連結,避免連結階段「找不到符號」錯誤。
反向情況下,C++ DLL 欲對 C 專案公開介面,同樣必須指定 C 連結。在 DLL 專案的函數定義前加上 extern "C":
extern "C" __declspec(dllexport) int MyFunc(long parm1);
這樣產生的符號名不經修飾,C 專案即可正常連結。但 C 程式碼無法直接呼叫 C++ 類別成員函數或使用 C++ 物件。常見解法是編寫 Wrapper 函數:以 C 連結導出的自由函數內部持有 C++ 物件指標,轉呼叫成員函數:
// Wrapper 函數:C 連結導出
extern "C" void call_Lib_CPPFunction(Lib* p, DataAttribute* dataAttribute) {
p->daFun(dataAttribute); // 實際呼叫 C++ 成員函數
}
// C++ 類別實現
void Lib::daFun(DataAttribute* dataAttribute) {
std::map<std::string, MMSINFO>::iterator it;
// ... 具體業務邏輯
}
此模式將 C++ 封裝性保留於 DLL 內部,對外僅暴露純 C 介面,實現語言隔離與二進位相容。
extern "C"。__cplusplus 宏,使單一標頭檔同時服務 C/C++。遵循上述模式,即可構建穩健、可維護的 C/C++ 混合專案架構。