在c++中调用C函数需用extern “C”禁用名称修饰,确保C链接;同样,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++两端对符号的理解完全相同。