C++中的extern "C"用法详解
简介
在C++中,存在着C和C++的二进制兼容性问题,即C++编译后的函数名与C编译后的函数名不一样。这会导致当我们在头文件中声明一个C++函数的时候,在C语言中无法使用这个函数。所以我们需要在C++ 中使用 extern "C"
关键字声明特定函数,以便在 C++ 环境下使用 C 标准程序声明及定义的函数。
用法
使用 extern "C"
关键字前需要知道两个概念:
- 名称修饰:C++ 编译器对函数名的处理是以一种叫做“名称修饰(Name Mangling)”的方式来实现的,这种方式的本质是在函数名前面加上符号“_”或者类似于“_Z”的字符来实现的。而在C语言中,由于不存在像C++中的函数重载等机制,所以编译器对函数名不做任何修饰。
- 函数签名:函数签名是指函数的类型信息,包括函数类型、参数列表、参数类型和返回类型等信息。
由于名称修饰的存在,我们定义的函数名与C语言的原型并不一致。通过 extern "C"
关键字,我们可以将名称修饰去掉,使得C语言可以调用这个函数并链接成功。
下面我们来看看 extern "C"
的使用例子。
示例1:在C++代码中定义函数并在C中使用
假设我们在C++中有以下代码:
// Sum.cpp
#include <iostream>
#include "sum.h"
int sum(int a, int b) {
std::cout << "Sum function in C++ is being called." << std::endl;
return a + b;
}
其中,头文件 sum.h
的内容为:
// sum.h
#ifndef SUM_H
#define SUM_H
extern "C" int sum(int a, int b);
#endif
在我们编写完以上代码后,在C语言中可以通过 below 的方式调用:
// main.c
#include <stdio.h>
#include "sum.h"
int main(void) {
int result = sum(1, 2);
printf("%d\n", result);
return 0;
}
则在链接的时候会提示找不到sum函数的符号,这个时候使用 extern "C"
来解决。
// Sum.cpp
#include <iostream>
#include "sum.h"
// 添加 extern "C" 修饰符
extern "C" {
int sum(int a, int b) {
std::cout << "Sum function in C++ is being called." << std::endl;
return a + b;
}
}
将 Sum.cpp
重新编译,并且在相应C编译器的编译选项(-I
选项)中添加 sum.h
的头文件路径,即可在C中使用。
示例2:C++和C通过动态库进行交互
前面一节只是简单地通过头文件来解决问题。我们还可以通过动态库(或称为共享库)的方式,将需要提供C语言调用的接口打包成一个动态库,让C语言程序调用该动态库,以进行功能的扩展。
在这里我们同样以 sum
函数为例,来展示C++和C之间通过动态库进行交互的方法。
// Sum.cpp
#include <iostream>
extern "C" {
int getSum(int a, int b) {
std::cout << "Sum function in dynamic library is being called." << std::endl;
return a + b;
}
}
在这里,我们将 getSum
作为动态库中的函数提供给C语言调用。
接下来,我们先将 getSum
字符串名称修饰去掉:
// Sum.cpp
#include <iostream>
extern "C" {
// 去掉名称修饰
__declspec(dllexport) int __stdcall getSum(int a, int b) {
std::cout << "Sum function in dynamic library is being called." << std::endl;
return a + b;
}
}
其中,__declspec(dllexport)
是 Windows 下的一个关键字(类似于 __attribute__((visibility("default")))
,用于指定该函数导出的符号),因为动态库在编译的时候需要明确地指定导出和导入,这也是和静态库最大区别。
接下来,我们可以通过以下步骤来实现动态库的编译,以及C语言程序的调用:
1.编译 Sum.cpp,产生 Sum.obj 文件:
> g++ Sum.cpp -fPIC -c -o Sum.o
其中,-fPIC
表示生成位置独立的代码,防止反汇编时找不到详细地址等信息。
2.使用下列命令将 Sum.obj 文件生成动态库文件 libsum.so:
> g++ -shared -o libsum.so Sum.o
3.编写C程序,调用动态库:
// main.c
#include <stdio.h>
#include <dlfcn.h>
int main() {
void *dlib = dlopen("./libsum.so", RTLD_NOW);
if (dlib == NULL) {
fprintf(stderr, dlerror());
exit(1);
}
typedef int (*funcptr)(int, int);
funcptr func = (funcptr)dlsym(dlib, "getSum");
printf("%d\n", (*func)(1, 2));
dlclose(dlib);
return 0;
}
其中, dlfcn.h
头文件包含了动态链接库的头文件,dlopen
函数可以用来打开动态库,dlsym
函数的参数 getSum
是动态库中提供给C语言的函数名,dlclose 是关闭链接。
通过上述步骤,我们实现了C++和C之间通过动态库进行交互的方法。
结论
通过在C++中使用 extern "C"
关键字,我们可以在C++环境下使用C标准程序声明及定义的函数,并且可以通过动态库的方式提供给C语言进行调用。通过多种实例的讲解,相信读者已经对C++中的 extern "C"
用法有了更为详细的理解。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++中的extern “C”用法详解 - Python技术站