Clang动态库导出C接口必须用extern "C"包裹声明,否则C++名称修饰导致C端链接失败;还需配合__attribute__((visibility("default")))、-fvisibility=hidden及纯C数据结构确保ABI兼容。

Clang动态库导出C接口必须用 extern "C" 包裹声明
不加 extern "C",C++ 编译器会对函数名做 name mangling(如 _Z10my_functionv),C 代码链接时根本找不到符号。动态库要被 C 程序调用,所有对外暴露的函数、结构体、宏定义,都得在头文件里用 extern "C" 声明。
典型写法是加条件编译保护,避免 C 编译器报错:
#ifdef __cplusplus
extern "C" {
#endif
<p>int my_add(int a, int b);
void my_process(const char* data, size_t len);</p><h1>ifdef __cplusplus</h1><p>}</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/gongju/2823" title="C++14">C++14</a>
<p>C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><h1>endif</h1><p>注意:这个包裹只作用于函数声明,不是定义;定义(实现)放在 .cpp 文件里,不用再写 extern "C"。
动态库编译时不能漏掉 -fvisibility=hidden 和显式导出
Clang 默认 visibility 是 default,所有符号都导出,但混用 C/C++ 时容易意外导出 C++ 符号(比如内联函数、模板实例),导致 C 端链接失败或运行时崩溃。必须主动控制可见性:
- 编译时加
-fvisibility=hidden - 对每个要暴露的 C 函数,在声明前加
__attribute__((visibility("default")))
头文件里应这样写:
#ifdef __cplusplus
extern "C" {
#endif
<p><strong>attribute</strong>((visibility("default"))) int my_add(int a, int b);
<strong>attribute</strong>((visibility("default"))) void my_process(const char* data, size_t len);</p><h1>ifdef __cplusplus</h1><p>}</p><h1>endif</h1><p>否则即使有 extern "C",符号也可能被隐藏,nm -D libxxx.so 看不到对应条目。
结构体和回调函数传参必须纯 C 风格
C 接口不认 C++ 类型,哪怕只是字段顺序或对齐差异也会出问题。常见陷阱包括:
- 结构体里不能有
std::string、std::vector、虚函数、非 POD 成员 - 回调函数指针类型必须用 C 函数指针语法,不能带
this或捕获 lambda - 字符串统一用
const char*,长度靠额外参数传,别用std::string_view
例如正确回调声明:
typedef void (*log_callback_t)(const char* msg, int level);
__attribute__((visibility("default"))) void set_logger(log_callback_t cb);
如果传了 std::function<void std::string></void>,C 端直接段错误。
链接时用 -shared -fPIC,且 C 调用端不能加 -lstdc++
Clang 生成动态库必须带 -shared -fPIC,否则加载失败。但更重要的是:C 程序链接这个库时,不能 加 -lstdc++ —— 因为你的接口全是 C 风格,没用任何 STL 或 RTTI,强制链接 libstdc++ 会引入不必要的依赖,甚至在嵌入式或 musl 环境下直接报 undefined reference to `__cxa_atexit'。
验证方式:
- 用
readelf -d libxxx.so | grep NEEDED确认没有libstdc++.so - 用
objdump -t libxxx.so | grep "my_add"确认符号是全局且未 mangling
真正麻烦的从来不是怎么写 extern "C",而是忘了它只管名字,不管内存布局、ABI 兼容性和符号可见性——这三个地方错一个,C 端就只能看到 undefined symbol。

















