C++ 中extern "C" 的使用

C++中,extern “C” 用于告诉编译器以C语言的方式来处理代码的连接,主要用于与C代码的互操作。C++和C在编译时处理函数和变量的方式不同,因此当C++程序需要调用C代码(例如标准库的C语言接口或其他C语言库)时,extern “C” 可以消除这些差异,使两者能够协同工作。

1、C++ 和 C 的符号修饰(Name Mangling)

C++编译器会对函数名称进行符号修饰(name mangling),以支持函数重载等特性。这种修饰会改变函数在目标代码中的名称,而C语言则不会进行这样的符号修饰。

#include<iostream>
using namespace std;

void foo(int a);  // C++中的函数声明

int main() {
  cout << "Hello, World!";
  return 0;
}

在C++中,foo(int) 可能会被编译器修饰为 _Z3fooi 或类似的名字,以便区分不同的重载版本。C语言中,foo 会直接编译为符号 foo,没有附加信息。

由于这种差异,C++编译器在链接C语言代码时无法找到对应的符号。extern "C"命令告知C++编译器以C的方式处理指定代码块中的符号,避免符号修饰。

2、extern "C" 的用法

extern "C" 用于告诉编译器按照 C 语言的方式进行函数名修饰(即“C 语言链接”)。这对于需要与 C 代码进行交互的场景非常有用,因为 C++ 编译器通常会对函数名进行“名称修饰”(Name Mangling),而 C 语言则不会。使用 extern "C" 可以让 C++ 函数名在链接时符合 C 语言的格式,从而与 C 代码兼容。

1) 单个函数声明

如只想将一个单独的函数声明为 C 函数,可以直接使用 extern "C" 关键字,

#include <iostream>

// 使用 extern "C" 将 foo 函数按 C 的方式链接
extern "C" void foo(int a);

// foo 函数的定义
void foo(int a) {
    std::cout << "Called foo with argument: " << a << std::endl;
}

int main() {
    foo(42);  // 调用 foo 函数
    return 0;
}

2)多个函数声明(代码块)

extern "C" 代码块中声明两个函数 foobar,然后在主函数中调用它们。

#include <iostream>

// 使用 extern "C" 代码块将多个函数声明为 C 链接方式
extern "C" {
    void foo(int a);
    void bar(double b);
}

// foo 函数的定义
void foo(int a) {
    std::cout << "Called foo with integer argument: " << a << std::endl;
}

// bar 函数的定义
void bar(double b) {
    std::cout << "Called bar with double argument: " << b << std::endl;
}

int main() {
    foo(42);        // 调用 foo 函数
    bar(3.14);      // 调用 bar 函数
    return 0;
}

3、主要用途

1)链接C语言的库

C++项目经常需要调用C语言库,如标准库(math.h)、POSIX库,或其他第三方C库。为了保证链接时C++编译器能够正确解析C函数的符号,需要在包含C库的头文件时使用 extern "C"

extern "C" {
    #include <math.h>
}

int main() {
    double result = sqrt(4.0);
    return 0;
}

使用 extern "C" 保证了 sqrt 等标准C库函数的符号能够被正确解析。

2) 在C++中定义C接口

如在C++中编写的代码需要提供给C程序调用,可以使用 extern "C" 来定义C兼容的接口,使C代码可以调用这些C++函数。

#include <iostream>

// 使用 extern "C" 定义一个 C 兼容接口
extern "C" void c_interface();

void c_interface() {
    // C++ 实现细节
    std::cout << "This is a C-compatible interface with a C++ implementation." << std::endl;
}

int main() {
    c_interface();  // 调用 C 兼容接口
    return 0;
}

4、extern "C" 的注意事项

1)只能用于函数和变量声明

extern "C" 只能用于函数和变量的声明,不能用于类、模板、命名空间等C++特有的特性。

2)无重载支持

由于C语言不支持函数重载,使用 extern "C" 声明的函数不能重载。

3)头文件中使用 extern "C"

为了在C++中包含C头文件,通常在C库的头文件中使用条件编译,以便在C和C++代码中都能正常使用。

推荐阅读
cjavapy编程之路首页