# extern "C"的作用
# 简要回答
extern "C" 是 C++ 编译器提供的一个链接规范(Linkage Specification),其主要作用是指示编译器按照 C 语言的规则来编译和链接其修饰的代码。
这主要体现在函数名修饰(Name Mangling)和调用约定(Calling Convention)上,目的是为了解决 C++ 与 C 以及其他语言在混合编程时的链接兼容性问题。
简单点说: extern "C" 用于告诉 C++ 编译器,被它修饰的函数或变量应使用 C 语言的编译和链接规则,即不进行名称修饰(Name Mangling),从而确保 C++ 代码可以正确链接到用 C 语言编译的库或目标代码。
# 详细回答
核心问题:**名称修饰 **(Name Mangling)
C++ 支持函数重载(Overloading),即多个函数可以有相同的名字但不同的参数列表。
为了在编译后的二进制代码中区分这些函数,编译器会对函数名进行复杂的修饰(Mangling),将参数类型、返回类型等信息编码到一个新的、唯一的内部名称中。
举例子,函数 void foo(int) 可能被修饰为 _Z3fooi。
C 语言不支持重载,因此没有名称修饰。
函数 void foo(int) 在编译后的符号表中名字仍然是简单的 foo。
这是链接时就会出现困境
当一个 C++ 程序(main.cpp)试图调用一个由 C 编译器编译的库(c_lib.a)中的函数 foo 时,会发生以下情况:
C++ 编译器在 main.cpp 中看到 foo(...),会对其进行名称修饰(例如,生成_Z3fooi )。
但在链接阶段,链接器会在 C 编译的库中**寻找符号 _Z3fooi **。
而 C 库中导出的符号名是未修饰的 foo。
结果:链接器找不到 _Z3fooi,报“undefined reference”错误。
extern "C" 的解决方案 通过在 C++ 代码中使用 extern "C" 来声明 C 库中的头文件,明确指示编译器:“请不要对这里面的函数名进行修饰,直接使用原始的 C 风格名称。”
这样,C++ 编译器生成的目标文件中寻找的符号名就是 foo,从而能与 C 库中的 foo 符号成功链接。
调用约定 (Calling Convention) 除了名称修饰,extern "C" 通常也意味着使用 C 语言的调用约定(如 __cdecl),这规定了函数参数如何压栈、栈由谁清理等底层细节,确保了二进制层面的兼容。
# 代码示例
// In my_class.h (C++ header)
class MyClass {
public:
int doSomethingComplex(int x);
};
// In c_interface.h (C-compatible header)
#ifdef __cplusplus
extern "C" {
#endif
int c_wrapper_do_something(int x);
#ifdef __cplusplus
}
#endif
// In c_interface.cpp
#include "my_class.h"
#include "c_interface.h"
extern "C" int c_wrapper_do_something(int x) {
MyClass obj; // 在C++内部使用类
return obj.doSomethingComplex(x); // 调用成员函数
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 知识拓展
- 知识图解

- 适用场景:
case1:调用 C 语言库:这是最经典的场景,如调用 Linux 的系统 API(glibc)、第三方 C 库(SQLite, Lua, libpng 等)。
case2:C++ 实现被 C 调用:如果你想用 C++ 写一个库,并希望它能被 C 程序调用,也需要在 C++ 的实现文件里用 extern "C" 来导出函数。
case3:与其它语言交互:许多脚本语言(如 Python、Lua)的解释器是用 C 编写的,它们的扩展接口(FFI)通常是 C 风格的。用 C++ 写这些扩展时,必须使用 extern "C" 来导出初始化函数,才能被宿主语言正确识别和加载。
评论
验证登录状态...