引言
在用C++(www.cppentry.com)的项目源码中,经常会不可避免的会看到下面的代码:
#ifdef __cplusplus
extern "C"
{
#endif
/*...*/
#ifdef __cplusplus
}
#endif
它到底有什么用呢,你知道吗?而且这样的问题经常会出现在面试or笔试中。下面我就从以下几个方面来介绍它:
- 1、#ifdef _cplusplus/#endif _cplusplus及发散
- 2、extern "C"
- 2.1、extern关键字
- 2.2、"C"
- 2.3、小结extern "C"
- 3、C和C++(www.cppentry.com)互相调用
- 3.1、C++(www.cppentry.com)的编译和连接
- 3.2、C的编译和连接
- 3.3、C++(www.cppentry.com)中调用C的代码
- 3.4、C中调用C++(www.cppentry.com)的代码
- 4、C和C++(www.cppentry.com)混合调用特别之处函数指针
1、#ifdef _cplusplus/#endif _cplusplus及发散
在介绍extern "C"之前,我们来看下#ifdef _cplusplus/#endif
_cplusplus的作用。很明显#ifdef/#endif、#ifndef/#endif用于条件编译,#ifdef _cplusplus/#endif
_cplusplus——表示如果定义了宏_cplusplus,就执行#ifdef/#endif之间的语句,否则就不执行。
在这里为什么需要#ifdef _cplusplus/#endif _cplusplus呢?因为C语言中不支持extern
"C"声明,如果你明白extern "C"的作用就知道在C中也没有必要这样做,这就是条件编译的作用!在.c文件中包含了extern
"C"时会出现编译时错误。
既然说到了条件编译,我就介绍它的一个重要应用——避免重复包含头文件。还记得腾讯笔试就考过这个题目,给出类似下面的代码(下面是我最近在研究的一个开源web服务器——Mongoose的头文件mongoose.h中的一段代码):
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*.................................
* do something here
*.................................
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* MONGOOSE_HEADER_INCLUDED */
| 01 |
#ifndef MONGOOSE_HEADER_INCLUDED
|
| 02 |
#define MONGOOSE_HEADER_INCLUDED
|
| 06 |
#endif /* __cplusplus */
|
| 08 |
/*.................................
|
| 10 |
| *.................................
| 15 |
#endif /* __cplusplus */
|
| 17 |
#endif /* MONGOOSE_HEADER_INCLUDED
*/ |
然后叫你说明上面宏#ifndef/#endif的作用?为了解释一个问题,我们先来看两个事实:
- 这个头文件mongoose.h可能在项目中被多个源文件包含(#i nclude
"mongoose.h"),而对于一个大型项目来说,这些冗余可能导致错误,因为一个头文件包含类定义或inline函数,在一个源文件中mongoose.h可能会被#i
nclude两次(如,a.h头文件包含了mongoose.h,而在b.c文件中#i nclude
a.h和mongoose.h)——这就会出错(在同一个源文件中一个结构体、类等被定义了两次)。
- 从逻辑观点和减少编译时间上,都要求去除这些冗余。然而让程序员去分析和去掉这些冗余,不仅枯燥且不太实际,最重要的是有时候又需要这种冗余来保证各个模块的独立。
为了解决这个问题,上面代码中的
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
/*……………………………*/
#endif /* MONGOOSE_HEADER_INCLUDED */
就起作用了。如果定义了MONGOOSE_HEADER_INCLUDED,#ifndef/#endif之间的内容就被忽略掉。因此,编译时第一次看到mongoose.h头文件,它的内容会被读取且给定MONGOOSE_HEADER_INCLUDED一个值。之后再次看到mongoose.h头文件时,MONGOOSE_HEADER_INCLUDED就已经定义了,mongoose.h的内容就不会再次被读取了。
2、extern "C"
首先从字面上分析extern "C",它由两部分组成——extern关键字、"C"。下面我就从这两个方面来解读extern "C"的含义。
2.1、extern关键字
在一个项目中必须保证函数、变量、枚举等在所有的源文件中保持一致,除非你指定定义为局部的。首先来一个例子:
//file1.c:
int x=1;
int f(){do something here}
//file2.c:
extern int x;
int f();
void g(){x=f();}
中g()使用的x和f()是定义在file1.c中的。extern关键字表明file2.c中x,仅仅是一个变量的声明,其并不是在定义变量x,并未为x分配内存空间。变量x在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。但是可以声明多