C++知识分享:C++项目中的extern "C" {}

在用C++的项目源码中,经常会不可避免的会看到下面的代码:

#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++互相调用

3.1、C++的编译和连接

3.2、C的编译和连接

3.3、C++中调用C的代码

3.4、C中调用C++的代码

4、C和C++混合调用特别之处函数指针

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 */

然后叫你说明上面宏#ifndef/#endif的作用?为了解释一个问题,我们先来看两个事实:

这个头文件mongoose.h可能在项目中被多个源文件包含(#include “mongoose.h”),而对于一个大型项目来说,这些冗余可能导致错误,因为一个头文件包含类定义或inline函数,在一个源文件中mongoose.h可能会被#include两次(如,a.h头文件包含mongoose.h,而在b.c文件中#include 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();}

在file2.c中g()使用的x和f()是定义在file1.c中的。extern关键字表明file2.c中x,仅仅是一个变量的声明,其并不是在定义变量x,并未为x分配内存空间。变量x在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。但是可以声明多次,且声明必须保证类型一致,如:

//file1.c:

int x=1;

int b=1;

extern c;

//file2.c:

int x;// x equals to default of int type 0

int f();

extern double b;

extern int c;

在这段代码中存在着这样的三个错误:

1.x被定义了两次

2.b两次被声明为不同的类型

3.c被声明了两次,但却没有定义

回到extern关键字,extern是C/C++语言中表明函数全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

与extern对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

喜欢此篇文章或觉得这篇文章对你有帮助的读者可以分享给身边的朋友们。如果你是小白也可以加入我们的学习QQ群:1026095781     领取大礼包一份,以及开发工具一份。

 

你可能感兴趣的:(C语言,C++,程序人生)