extern "C" 用于解决C++与C混合编程时的函数名修饰冲突,它强制C++按C语言规则导出和查找符号,避免链接时出现undefined reference或multiple definition错误。

extern "C" 是用来解决函数名修饰冲突的
你写好了一个 C 的 libmath.a,里面有个 add 函数,用 C++ 代码去链接时却报 undefined reference to 'add'——不是函数没实现,是 C++ 编译器把函数名悄悄改成了类似 _Z3addii 这种带类型信息的名字,而 C 链接器只认原始的 add。加 extern "C" 就是告诉 C++:“别修饰这个名字,按 C 的规则导出和查找”。
常见错误现象:undefined reference to 'xxx'(但头文件已包含、库也链接了)、multiple definition of 'xxx'(C 和 C++ 各自定义了一次同名函数)。
- 只对函数声明或定义加
extern "C",不能加在函数体内部 - 头文件里写
extern "C"要包一层#ifdef __cplusplus,否则 C 编译器会报错 - 如果头文件同时被 C 和 C++ 源文件包含,必须用条件宏包裹,否则 C 编译不过
在 C++ 中调用 C 库的标准写法
假设你有一个 C 头文件 utils.h,内容是纯 C 函数声明。C++ 源文件想安全包含它,就得让编译器“切换语言视角”。
正确做法不是改 C 头文件本身(那会破坏 C 项目的独立性),而是在 C++ 文件里用 extern "C" 包裹 #include:
立即学习“C语言免费学习笔记(深入)”;
extern "C" {
#include "utils.h"
}
或者更推荐的方式:在 C 头文件末尾加上标准防护:
#ifdef __cplusplus
extern "C" {
#endif
int do_work(int x);
#ifdef __cplusplus
}
#endif
这样 C++ 文件直接 #include "utils.h" 就行,不用每次手动包一层。
在 C 中调用 C++ 函数要反向处理
C 编译器不认识 extern "C",所以不能在 C 文件里写它;反过来,C++ 函数要被 C 调用,必须在 C++ 侧显式声明为 C 链接方式。
关键点:只能对非成员函数、非模板函数、不抛异常的函数使用 extern "C"。类方法、重载函数、返回 std::string 的函数都不行。
- 在 C++ 源文件中定义函数时,用
extern "C"修饰函数定义(不只是声明) - 对应头文件中也要有相同声明,且同样需加
#ifdef __cplusplus防护 - 函数参数和返回值类型必须是 C 兼容的:比如用
const char*而不是std::string,用int而不是std::optional<int>
链接时符号不匹配的排查路径
遇到链接失败,别急着改代码,先看符号到底长什么样。用工具确认实际导出名,比猜更可靠。
Linux/macOS 下查 C 库符号:nm -C libmath.a | grep add(-C 表示 demangle,看清是不是原始名);查 C++ 目标文件符号:nm -C main.o | grep add,对比两边是否一致。
- 如果 C 库显示
T add,而 C++ 目标文件找的是U _Z3addii,说明 C++ 侧漏了extern "C" - 如果两者都显示
T add,但还报错,可能是库没加进链接命令(-lmath漏了或路径不对) - Windows 下用
dumpbin /symbols替代nm,逻辑一样
最常被忽略的是头文件没做 __cplusplus 防护,导致 C 编译时报语法错误,而开发者只盯着 C++ 侧改,来回折腾半天。











