编译成动态库
.dll / .so 与 extern "C"
商用
◎学完你会
- 把 C++ 代码编译成动态库(Windows .dll / Linux .so);
- 用
extern "C" 暴露稳定 C 接口给其他语言调用;
- 理解为什么互操作要避免 C++ 特有的符号重载/异常外抛。
1C++ 源码 → 动态库
编译时加 -shared -fPIC(Linux)或 /LD(MSVC),产出 .so / .dll,供 Python 等语言加载。
g++ -shared -fPIC -o libx.so x.cpp
extern "C" int add(int a, int b);
2关键命令
| 写法 | 含义 |
g++ -shared -fPIC -o libx.so src.cpp | Linux 编译成共享库(.so);-fPIC 位置无关代码 |
cl /LD src.cpp | Windows MSVC 编译成 DLL |
extern "C" { int fn(...); } | 让函数按 C 命名规则导出(不 name-mangling),其他语言才好调用 |
__declspec(dllexport) | Windows 显式导出符号 |
✅ extern "C" 是关键:C++ 会把函数名改写成带参数类型的符号(name mangling),Python ctypes 找不到;包一层 C 接口符号就稳定可查。
3为什么互操作走 C 接口
C 接口是最低公共标准:C 无重载、无名字改编、无 RAII 析构、异常不跨语言。跨语言边界要简单值类型 + 显式生命周期,别让异常/智能指针/重载直接暴露出去。
extern "C"{
int add(int a,int b){ return a+b; } // 简单、稳定、可被 ctypes 找到
void* create(); void destroy(void*); // 对象用句柄+成对创建/销毁
}
// 不要在边界暴露:重载、模板、智能指针、跨语言抛异常
要点:跨语言调用尽量用 POD / C 兼容类型(int、double、指针);对象封装成不透明句柄(void* + create/destroy)。C++ 特性(重载、异常)一律留在 C++ 内部,边界只留 C 函数。
!易错点
① 忘了 extern "C"——C++ name mangling 后 Python 按名找不到函数。
② 边界暴露重载/模板——同名多符号,其他语言无法简单调用。
③ 跨语言抛 C++ 异常——异常机制不互通,会未定义行为/崩溃。
④ Windows 忘 dllexport / 忘了 32/64 位匹配——导出不到或位数不符加载失败。
?跨学科:动态库接口,像"工厂对外销售的标准件"
把 C++ 编成 .dll/.so 像工厂生产标准配件:自己内部用复杂工艺(重载、模板、智能指针),但对外只卖统一规格的标准件(extern "C" 的 C 函数:int add(int,int)),任何"买家"(Python/其他语言)按说明书就能用。
extern "C" 像给配件贴上统一型号标签(不 mangling),否则 C++ 给每款加料写一堆复杂型号名,别人根本对不上号。这也是"边界只留简单稳定接口、复杂东西藏内部"的工程智慧。
✎练一练
Linux 下把 C++ 编译成动态库(.so)的命令是?
Linux 动态库用 -shared -fPIC 编译成 .so;-c 只出目标文件。
extern "C" 包裹函数的主要作用是?
extern "C" 去掉 C++ 的名字改编,导出稳定的 C 符号,跨语言才好调用。
跨语言(如 C++/Python)边界不宜直接暴露的是?
跨语言边界用 POD/C 兼容类型 + 显式生命周期;重载/模板/智能指针/跨语言异常要留在 C++ 内部。