extern "C" 的作用

3,924 阅读2分钟

extern "C" 的作用是为了能够正确在 C++ 代码中调用 C 语言代码。 加上 extern "C" 后指示编译器按 C 编译器编译这部分代码。使用它的本质的原因是 C++ 函数重载,C++ 中函数重载在编译后生成的不止是函数名,也会带上参数类型。而 C 编译器编译函数时不会带上函数的参数类型。看下如下的例子:

// MyMath 头文件声明
#ifndef MyMath_h
#define MyMath_h

#include <stdio.h>

int add(int a, int b);

#endif /* MyMath_h */


// MyMath.c 实现文件
#include "MyMath.h"

int add(int a, int b)
{
    return a + b;
}

请注意以上是 C 代码编写的。下面的 main 函数是 C++ 代码。

// C++ main  函数
#include <iostream>
#include "MyMath.h"

int main(int argc, const char * argv[]) {
    // insert code here...
    
    using std::cout;
    using std::endl;
            
    cout << "add 3 2: " << add(3, 2) << endl;
    
    
    return 0;
}

本案例在 XCode 上是用 clang 编译器。产生错误如下:

告诉我们找不到 add(int, int), 前面说过是因为 C++ 函数重载的问题导致的,这里我们通过生成汇编代码看一下函数编译后会变成什么。

clang -S MyMath.c 

可以看到 add 变为了 _add。

接下来我们将 MyMath.c 改为 MyMath.cpp 文件,再进行汇编,可以看到变成了 __Z3addii。

上面的代码引入后头文件 MyMath.h 会发生什么?看看汇编就知道了。

clang++ -S main.cpp -o main.s

上面的图不就说明了情况吗? 链接时它要找的是 __Z3addii 函数,而我们编译生成的是 _add。 这就会导致错误了。 那怎么解决呢?

这就是 extern "C" 的作用了,前面说过 extern "C" 包含的代码会用 C 的编译器来编译。我们将上面改为

#include <iostream>
// 使用 C 的编译器来编译.
extern "C" {
#include "MyMath.h"
}

int main(int argc, const char * argv[]) {
    // insert code here...
    
    using std::cout;
    using std::endl;
            
    cout << "add 3 2: " << add(3, 2) << endl;
    
    
    return 0;
}

口说无凭, 继续汇编。

clang++ -S main.cpp -o main.s

各位看到了吗? 这里就改为了 _add, 这样就能在链接时找到了。

在日常开发中尤其要注意 C++ 代码中使用到了 C 编译的库时,笔者在使用 ffmpeg 中就踩过这个坑。