核心問題:名字修飾
- C 編譯器:
void foo(int, int) → 符號 _foo(或 foo)。
- C++ 編譯器:為支持重載,生成
_foo_int_int 等修飾名(mangled name),包含參數類型信息。
- 直接混編:C++ 尋找
_foo_int_int,C 庫提供 _foo → 鏈接失敗 undefined reference。
extern "C" 解決方案
// 告訴 C++ 編譯器:大括號內按 C 規則編譯/鏈接(不修飾名字)
extern "C" {
void foo(int x, int y); // 符號為 _foo
int bar(double d);
}
- 作用於聲明,指導 C++ 編譯器生成 C 風格符號引用。
- 定義端(.c 文件)無需
extern "C",正常編譯即產生 C 符號。
標準跨語言頭文件模板
// foo.h —— 同時被 .c 和 .cpp 包含
#ifndef FOO_H
#define FOO_H
#ifdef __cplusplus
extern "C" {
#endif
// 這裡放純 C 接口:函數聲明、結構體定義(無方法)、宏
void foo(int x, int y);
int compute(double *data, size_t n);
#ifdef __cplusplus
}
#endif
#endif // FOO_H
完整編譯鏈接演示
// foo.c (C 實現)
#include "foo.h"
#include
void foo(int x, int y) { printf("sum=%d\n", x+y); }
int compute(double *d, size_t n) { /* ... */ return 0; }
// test.cpp (C++ 調用)
#include "foo.h"
#include
int main() {
foo(1, 2); // 直接調用
return 0;
}
// 編譯命令
gcc -c foo.c -o foo.o
g++ -c test.cpp -o test.o
g++ test.o foo.o -o test // 鏈接成功
./test // 輸出 sum=3
進階場景
C++ 類導出 C 接口(工廠模式)
// foo.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Opaque *Handle; // 不透明指針
Handle create_object();
void destroy_object(Handle h);
int do_work(Handle h, int param);
#ifdef __cplusplus
}
#endif
// foo.cpp
#include "foo.h"
#include "MyClass.hpp"
extern "C" Handle create_object() {
return reinterpret_cast(new MyClass());
}
extern "C" void destroy_object(Handle h) {
delete reinterpret_cast(h);
}
extern "C" int do_work(Handle h, int p) {
return reinterpret_cast(h)->work(p);
}
常見誤區
- 以為定義端也要加:.c 文件不需也不應加
extern "C"。
- 用於 C++ 類成員函數:成員函數有
this,無法直接用 extern "C",需包裝為自由函數。
- 忽略頭文件保護:
#ifndef/__cplusplus 結構確保 .c/.cpp 都能安全包含。