在C++中调用C函数需用extern "C"禁用名称修饰,确保C链接;同样,C++定义供C调用的函数也须用extern "C"声明。

在C++中调用C语言函数,关键在于解决C++的名称修饰(name mangling)问题。C++编译器会对函数名进行修饰以支持重载、命名空间等特性,而C编译器只使用原始函数名。使用 extern "C" 就是为了告诉C++编译器:“这部分函数名不要修饰,按C的方式链接”。
一、在C++代码中声明C函数(最常见场景)
当你有现成的C头文件(如 math.h、stdio.h),或自己写的C库头文件(比如 utils.h),需要在C++中调用时,需确保函数声明被 extern "C" 包裹:
- 如果C头文件本身已适配C/C++混合编译,通常会这样写:
#ifdef __cplusplus
extern "C" {
#endif
void c_function(int x);
int another_c_func(const char* s);
ifdef __cplusplus
}
endif
立即学习“C语言免费学习笔记(深入)”;
这样在C++中直接 #include "utils.h" 就能安全调用。
- 如果C头文件没加保护(比如第三方纯C头文件没考虑C++),你可以在C++源文件中手动包裹:
extern "C" {
#include "legacy_c_header.h" // 假设它只有C函数声明,无C++语法
}
// 然后就可以调用其中的函数了
c_legacy_init();
c_legacy_process(data);
二、在C++中定义供C调用的函数
如果你在C++源文件里写了一个函数,希望让C代码也能调用它,必须用 extern "C" 声明其链接规范:
- 声明和定义都需加 extern "C"(推荐在头文件中声明):
// mycppfunc.h
#ifdef __cplusplus
extern "C" {
#endif
void cpp_implementation(int value); // C可见的接口
ifdef __cplusplus
}
endif
立即学习“C语言免费学习笔记(深入)”;
// mycppfunc.cpp
#include "mycppfunc.h"
extern "C" void cpp_implementation(int value) {
// 实际C++实现(可使用std::vector、new等)
std::cout << "Called from C: " << value << std::endl;
}
注意:函数体内部可以自由使用C++特性,但签名必须是C兼容的(不能含引用、类类型、重载、模板等)。
三、链接C静态库或动态库时的注意事项
即使头文件声明正确,链接阶段出错(如 undefined reference to 'xxx')往往是因为符号名不匹配:
- 确认C库是用C编译器(如gcc)编译的,不是用g++(否则可能隐含C++链接规则);
- 链接时顺序重要:C++目标文件放在前,C库放在后(尤其用gcc/g++链接时);
- 若用CMake,确保C库被当作C库处理(add_library(... INTERFACE) 或显式设置 set_property(... PROPERTY LANGUAGE C));
- 可用 nm -C libxxx.a(Linux)或 dumpbin /symbols(Windows)检查目标文件中导出的符号名是否未修饰(即显示为 cpp_implementation 而非 extern "C" { void func(); } void func() { ... } —— 声明用了extern "C",定义却没加,链接失败;
- 在类内用 extern "C" 声明成员函数 —— 不合法(C链接不支持this指针);
- 试图用 extern "C" 导出模板函数或重载函数 —— C不支持,编译报错;
- 头文件中写了 extern "C" 但忘了加 #ifdef __cplusplus 保护,导致纯C编译失败。
不复杂但容易忽略。核心就一条:让声明和定义的链接规范严格一致,并确保C和C++两端对符号的理解完全相同。











