extern "C" {}
的用法。
功能
extern "C" {}
是C++的规范(C编译器无法识别),它告诉C++编译器,这些代码要按照C的规范(rule)
来处理。主要是function name的mingle机制,C++有mingle而C没有。在C++中,源码中的函数名会
被编译器混合,鬼才知道最终二进制文件中的函数命到底是什么,而且不同的编译器有不同的混合方法,
这就更没处猜了。所以要想连接C和C++,必须要有一个机制来保证两者可以相互识别,
这就是extern "C" {}
要做的事情。
注意extern "C" {}
只是告诉C++编译器要按照C的规范来处理代码,但是这些代码仍然是C++编译器来处理,
所以这些代码不能更C++规范冲突,如不能使用C++的关键字来命令变量,你不能定义一个名为class
的变量。
用法
用法包括两部分:
- 在C代码中调用C++函数
- 在C++代码中调用C函数
在C代码中调用C++函数
C++编译器生成的二进制文件中,函数名已经不是头文件中声明的样子了,所以要想让C代码来调用这些函数,
必须保证二进制文件中的函数名跟头文件中的函数名一致,这就要求C++代码中声明函数时使用extern "C" {}
。
在C++代码中调用C函数
C编译器不会进行函数名混合,最终的二进制文件中的函数名就是源码中声明的函数名,但是C++编译器在
进行调用时也是默认进行混合的,所以C++源码中print();
这个函数调用被编译之后可能会变成_VprintV()
这么一个调用,所以必须当C++代码中调用C函数时,必须让C++编译器知道,
这个函数是按照C规范定义的,这样它就被会被混合了。
注意C库必须是用C编译器编译的,当然也可以用C++编译器编译,不过这样的话那就不叫C库。
如何写头文件
总而言之,问题的关键还是C++编译器在生成、调用函数时都会进行函数名混合,导致找不到符号。
那么该如何操作呢?关键还是头文件的写法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// header.h
#ifndef HEADER_H
#define HEADER_H
#ifdef __cplusplus
#define EXTERN_C extern "C"
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C /* Nothing */
#define EXTERN_C_BEGIN /* Nothing */
#define EXTERN_C_END /* Nothing */
#endif
EXTERN_C void foo(void);
EXTERN_C_BEGIN
int bar(int);
EXTERN_C_END
#endif
这段代码的好处是,该头文件既可以被C编译器使用,也可以被C++编译器使用,因为extern "C"
无法
被C编译器处理,所以用宏来分开处理。
具体实现的话,可以用C来实现(可以用C编译器也可以用C++编译器进行编译),也可以用C++来实现,
至于使用,可以在C代码中使用,也可以在C++代码中使用。
最后说明一下,除了函数名混合,C,C++还有一些其他区别,如void func()
,在C编译器看来,func
函数可以没有参数,也可以接受任意多个参数(非可变参数),但是C++编译器看来,func
不能
接受任何参数。
实例
作为一个实例,头文件里面定义接口,然后分别用C、C++来实现,最后分别做测试,在C代码中调用
C++库,在C++代码中调用C库。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// def.h 接口
#ifndef DEF_H
#define DEF_H
#ifdef __cplusplus
#define EXTERN_C extern "C"
#define EXTERN_C_BEGIN extern "C" {
#define EXTERN_C_END }
#else
#define EXTERN_C /* Nothing */
#define EXTERN_C_BEGIN /* Nothing */
#define EXTERN_C_END /* Nothing */
#endif
EXTERN_C_BEGIN
void print();
EXTERN_C_END
#endif
然后是C版本的实现,1
2
3
4
5
6// my.c
#include <stdio.h>
#include "def.h"
void print() {
printf("c print\n");
}
以及C++版本的实现,1
2
3
4
5
6// my.cpp
#include <iostream>
#include "def.h"
void print() {
std::cout << "cpp print" << endl;
}
最后是测试,C代码中调用C++库函数,1
2
3
4
5
6
7// test.c
#include "def.h"
int main()
{
print();
return 0;
}
以及C++代码中调用C库函数,1
2
3
4
5
6
7// test.cpp
#include "def.h"
int main()
{
print();
return 0;
}
Makefile文件,1
2
3
4
5
6
7
8
9
10
11
12
13all: testc testcpp
testc : test.c libmycpp.so
cc -o testc test.c -L. -lmycpp
libmycpp.so : my.cpp
g++ -shared -fpic -o libmycpp.so my.cpp
testcpp : test.cpp libmyc.so
g++ -o testcpp test.cpp -L. -lmyc
libmyc.so : my.c
cc -shared -fpic -o libmyc.so my.c
clean :
rm -f *.so testc testcpp
(over)