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 中就踩过这个坑。