extern "c" 是解决c++与c混合编程中名字修饰冲突的关键机制,用于禁用c++的name mangling,确保函数和变量符号名与c兼容,适用于函数、变量声明及动态库加载等场景。

extern "C" 是用来解决名字修饰冲突的
C++ 编译器会对函数名做 name mangling(名字修饰),比如把 void foo(int) 编译成类似 _Z3fooi 的符号;而 C 编译器生成的是直接的 foo。链接时如果 C++ 代码直接调用 C 库的函数,链接器找不到匹配符号,就会报 undefined reference to 'foo' 这类错误。
- 必须在 C++ 代码里用
extern "C"告诉编译器:“接下来这些声明不要做 name mangling” - 只对函数声明和变量声明有效,不能用于类、模板或内联函数
- 如果头文件是给 C 和 C++ 共用的,常见写法是加宏判断:
#ifdef <strong>cplusplus<br>extern "C" {<br>#endif<br><br>// C 函数声明<br>int some_c_function(double);<br><br>#ifdef </strong>cplusplus<br>}<br>#endif
调用 C 静态库时,头文件必须包裹 extern "C"
哪怕你已经用 g++ 链接了 libmath.a,如果它的头文件没用 extern "C" 包裹,C++ 代码照样连不上。
- 不要只在自己的 .cpp 文件里临时加
extern "C"去包一个#include—— 这只能影响该文件内的声明,但库本身的符号还是 C 风格,不匹配 - 正确做法是确保你包含的 C 头文件本身已处理好兼容性,或者你自己写一层 wrapper 头文件
- 示例错误写法:
extern "C" {<br>#include "c_library.h" // ❌ 大多数情况下无效,因为头里可能已有函数定义或宏展开<br>} - 推荐写法:自己建
my_c_wrapper.h,里面明确声明需要的函数:extern "C" {<br>int c_calculate(int a, int b);<br>}
动态库(.so / .dll)加载时也绕不开 extern "C"
用 dlopen + dlsym 手动加载 C 动态库时,虽然跳过了编译期链接,但 dlsym 查找的符号名仍是 C 风格的裸名。如果你在 C++ 源码里声明了没加 extern "C" 的函数指针类型,调用时会因类型不匹配崩溃或静默出错。
本文档主要讲述的是Android中JNI编程的那些事儿;JNI译为Java本地接口。它允许Java代码和其他语言编写的代码进行交互。在android中提供JNI的方式,让Java程序可以调用C语言程序。android中很多Java类都具有native接口,这些接口由本地实现,然后注册到系统中。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
- 函数指针声明必须和实际符号一致:
extern "C" {<br>typedef int (*calc_func_t)(int, int);<br>}<br>// ✅ 类型定义也在 extern "C" 块里<br>// ❌ 如果 calc_func_t 在 extern "C" 外定义,它会被当 C++ 类型处理 - Windows 上用
GetProcAddress同理,typedef和调用都要在extern "C"下
容易被忽略的细节:全局变量和 const 修饰符
C++ 里 const 全局变量默认是 internal linkage(内部链接),而 C 里 const int x = 42; 是 external linkage。如果 C 库导出了一个 const char* version_str,C++ 侧没用 extern "C" 声明,不仅符号找不到,还可能因 linkage 差异导致 ODR(One Definition Rule)问题。
立即学习“C语言免费学习笔记(深入)”;
- C 中的全局变量必须显式用
extern "C"声明,包括const变量 - 不要依赖“变量名简单就不用 extern "C"”——C++ 标准不保证未修饰的
const变量能被 C 符号正确引用 - 示例:
extern "C" {<br>extern const char* c_version; // ✅ 必须 extern + extern "C"<br>}
C++ 和 C 的 linkage 规则差异比表面看起来更顽固,尤其在跨编译单元、模板实例化或隐式内联场景下,extern "C" 漏掉一处,就可能让链接器在最后一步突然失败,而且错误信息往往不指向真正原因。









