c++oding="utf-8" ?>
extern "c"用于解决c/c++跨语言链接时的名字修饰不匹配问题:c++编译器对函数名进行name mangling(如void foo(int)→_z3fooi),而c编译器保留原名(foo),导致链接时报“undefined reference”;extern "c"强制c++以c规则生成符号,确保链接成功。

extern "C" 是用来解决名字修饰问题的
C++ 编译器会对函数名做 name mangling(名字修饰),比如 void foo(int) 可能被编译成 _Z3fooi 这类符号;而 C 编译器只保留原始函数名 foo。直接在 C++ 里 #include 一个 C 头文件并调用,链接时大概率报 undefined reference to 'foo' —— 因为两边符号对不上。
在 C++ 里声明 C 函数的正确写法
必须用 extern "C" 告诉 C++ 编译器:“这段声明里的函数,按 C 的规则找符号”。常见写法是包一层条件宏:
#ifdef __cplusplus
extern "C" {
#endif
<p>void c_function(int x);
int another_c_api(const char* s);</p><h1>ifdef __cplusplus</h1><p>}</p><h1>endif</h1><p></p>
如果你只是临时调用一两个 C 函数(比如系统 API),也可以不改头文件,直接在 C++ 源文件里 inline 声明:
extern "C" {
int printf(const char*, ...);
void free(void*);
}
注意:extern "C" 只影响**链接符号名**,不影响类型检查、重载或内联行为 —— C 函数在 C++ 里依然不能重载。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
常见踩坑点:头文件没加 extern "C" 包裹
很多 C 库头文件本身没考虑 C++,比如你写了 #include "some_c_lib.h",但那个头文件里全是裸声明:void init(); int process();。这时候 C++ 编译器会按 C++ 规则生成符号,链接失败。
解决办法不是去改别人的头文件(通常也不允许),而是自己包一层:
extern "C" {
#include "some_c_lib.h"
}
或者更稳妥地,在包含前加宏定义(如果该库支持):
#define SOME_C_LIB_H_NO_EXTERN_C
extern "C" {
#include "some_c_lib.h"
}
另外注意:C++ 中 extern "C" 块里不能出现 C++ 特有语法,比如模板、类、std::string 参数 —— 这些没法被 C 看懂,也违背了“C 接口”的本意。
动态链接时符号可见性也要匹配
如果你自己写 C 函数并编译成 .so/.dll 供 C++ 调用,除了头文件加 extern "C",源文件里实现也得确保导出的是 C 风格符号:
- C 源文件(
impl.c)里写void my_c_func() { ... }就行,天然 C 符号 - C++ 源文件(
impl.cpp)里实现 C 接口时,必须用extern "C"修饰定义:extern "C" void my_c_func() { // 实现 } - Windows 下若用
__declspec(dllexport),要放在extern "C"块内,否则导出的还是 C++ 符号
真正容易被忽略的是:即使声明和实现都加了 extern "C",如果构建时 C 和 C++ 目标文件用了不同 ABI(比如一个用 libc++,一个用 libstdc++),或者混用了不同编译器(MSVC vs GCC),照样可能链接失败 —— 这时候得查实际生成的符号名:nm -C libxxx.a | grep my_c_func 看是不是真成了 C 风格。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










