windows下dll必须显式声明__declspec(dllexport),否则默认不导出任何符号,导致lnk2019或运行时“procedure not found”错误;linux/macos的.so默认导出所有非静态全局符号,但应启用-fvisibility=hidden并显式标记导出以控制abi。

Windows 下 DLL 导出必须显式声明 __declspec(dllexport)
Linux/macOS 的 .so 默认导出所有非静态全局符号,但 Windows 的 DLL 默认不导出任何符号——哪怕你写了 extern "C" 也没用。漏加 __declspec(dllexport) 是跨平台项目在 Windows 上链接失败的头号原因。
常见错误现象:LNK2019: unresolved external symbol(调用方找不到符号),或运行时报 The specified procedure could not be found(DLL 加载成功但函数没导出)。
实操建议:
- 用宏统一控制导出:定义
MYLIB_EXPORT,在 Windows 编译时展开为__declspec(dllexport),其他平台为空 - 导出 C++ 类时,整个类声明前加该宏;导出自由函数时,函数声明前加(不是定义处)
- 避免对模板函数/类直接加
dllexport——它们实例化发生在使用点,DLL 内无法预知哪些特化会被需要,应改用非模板接口或显式实例化
用 extern "C" 抑制 C++ 名字修饰(Name Mangling)
不同编译器对同一函数生成的符号名完全不同,比如 void foo(int) 在 MSVC 可能变成 ?foo@@YAXH@Z,在 GCC 变成 _Z3fooi。跨语言调用(如 Python ctypes、C# P/Invoke)或混用不同编译器产出的库时,这会导致链接失败或运行时崩溃。
实操建议:
- 仅对需要稳定 ABI 的函数加
extern "C",不要包裹整个头文件(否则 C++ 重载、类成员函数失效) - 在头文件中用
#ifdef __cplusplus包裹,保证 C 和 C++ 都能正确包含:#ifdef __cplusplus extern "C" { #endif void my_c_api_func(int x); #ifdef __cplusplus } #endif - 注意:
extern "C"不影响结构体布局或调用约定,若需兼容不同 ABI,还需显式指定__cdecl或__stdcall(尤其 Windows API 风格)
Linux/macOS 上需启用 -fvisibility=hidden 并显式标记导出
默认导出所有符号不仅增大二进制体积,更会暴露内部实现细节,造成 ABI 耦合。比如某内部辅助函数意外被导出,后续版本删掉它就会导致下游链接失败。
实操建议:
- 编译时加
-fvisibility=hidden,再对真正要导出的函数/类加__attribute__((visibility("default"))) - 与 Windows 宏统一:定义
MYLIB_EXPORT,在 Linux 上展开为__attribute__((visibility("default"))) - 避免对内联函数、模板、lambda 使用 visibility 属性——它们本就不生成独立符号,加了也无效,还可能触发编译警告
- 调试时可用
nm -D libmylib.so检查实际导出符号列表,确认无多余符号泄露
链接器重定位失败常因运行时路径未配置或依赖缺失
动态库加载时找不到依赖(如 libstdc++.so.6 或另一个自定义 .so),或主程序找不到动态库本身,都会表现为 undefined symbol 或 cannot open shared object file。这不是编译问题,而是部署环境问题。
实操建议:
- Linux 上用
ldd your_program查看依赖树,确认所有=> not found条目;用readelf -d libxxx.so | grep RPATH检查是否嵌入了正确的搜索路径 - 设置
RPATH:编译时加-Wl,-rpath,'$ORIGIN'(让程序优先从自身目录找依赖),或-Wl,-rpath,/your/lib/path - macOS 上对应的是
-rpath和@rpath,且需用install_name_tool修改库的 install name,否则dyld不认 - Windows 上没有等价机制,靠
PATH或同目录放置,别依赖当前工作目录——服务进程的工作目录不可控
符号导出和重定位看着是编译链接阶段的事,但真正出问题往往在运行时环境里。一个没设 RPATH 的 Linux 库,编译通过、测试通过,上线后第一秒就挂;一个忘了加 __declspec 的 Windows DLL,本地开发一切正常,CI 构建机器上却连不上。这些坑都藏在“应该能跑”的假设里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











