linux下用g++制作.so需加-fpic和-fvisibility=hidden,显式标记default导出函数;windows下msvc需用__declspec(dllexport)配合extern "c";跨平台须规避name mangling、stl容器返回及abi不兼容问题。

Linux 下用 g++ 制作 .so 动态库
动态库不是“编译完就能用”,得让符号导出、避免隐藏、指定接口可见性。默认 g++ 会把所有符号设为 hidden,g++ -shared 本身不解决这个问题。
- 必须加
-fPIC:生成位置无关代码,否则链接时报错relocation R_X86_64_32 against symbol - 推荐加
-fvisibility=hidden,再对需要导出的函数显式标记<strong>attribute</strong>((visibility("default"))),避免意外导出内部符号 - 库文件名要带版本号,比如
libcalc.so.1.0,再建软链libcalc.so → libcalc.so.1,否则运行时找不到 - 编译命令示例:
g++ -fPIC -fvisibility=hidden -shared -o libcalc.so.1.0 calc.cpp
Windows 下用 MSVC 制作 .dll 并导出函数
Windows 的 DLL 导出比 Linux 更显式,不声明就完全不可见。不能只靠 __declspec(dllexport) 写在定义上——头文件里没对应声明,调用方照样链接失败。
- 头文件中必须用宏控制导出/导入声明,例如:
#ifdef BUILDING_CALC_DLL#define CALC_API __declspec(dllexport)#else#define CALC_API __declspec(dllimport)#endif - 函数声明前加
CALC_API,如CALC_API int add(int a, int b); - 编译 DLL 时定义
BUILDING_CALC_DLL宏;调用方不定义,走dllimport分支 - 生成的
.dll和.lib(导入库)都要保留,缺.lib会导致链接时报LNK2019: unresolved external symbol
Linux 程序如何正确链接并加载 .so
链接时用 -l 和 -L 只解决编译期依赖,运行时找不到库是另一个常见问题,和 LD_LIBRARY_PATH、/etc/ld.so.cache、rpath 都有关。
- 编译链接命令:
g++ main.cpp -L. -lcalc -o main(假设libcalc.so在当前目录) - 运行前确保系统能定位到库:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH是临时方案,但不推荐上线使用 - 更稳妥的是编译时嵌入 rpath:
g++ main.cpp -L. -lcalc -Wl,-rpath,'$ORIGIN' -o main,这样程序会从自身所在目录找 so - 检查依赖是否解析成功:
ldd main,如果某库显示not found,说明运行时路径配置错了
跨平台调用动态库时最常踩的坑
C++ 名字修饰(name mangling)会让导出函数名变成一串无法预测的符号,比如 _Z3addii,直接 dlsym 或 GetProcAddress 找不到。
- 所有要被外部调用的函数,必须用
extern "C"包裹声明和定义,禁用 C++ 修饰 - Windows 下 DLL 的
extern "C"还得配合__declspec(dllexport),顺序不能错:extern "C" { CALC_API int add(int, int); } - Linux 下用
dlopen/dlsym时,函数指针类型必须严格匹配,否则运行时崩溃,建议用 typedef 定义函数类型 - 不要在动态库中返回 STL 容器(如
std::string、std::vector),调用方和库用不同标准库版本时内存布局不兼容,极易 crash
C++ 动态库真正的难点不在编译命令,而在于 ABI 稳定性、符号可见性控制、以及跨模块内存管理边界——这些地方一旦出错,现象往往是段错误或静默数据损坏,调试成本远高于编译失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











