extern c详解(上)

文章修改自:<wbr style="line-height:25px"><a rel="nofollow" href="http://wenku.baidu.com/view/8872c444b307e87101f69650.html" style="color:rgb(207,121,28); line-height:25px; text-decoration:none">http://wenku.baidu.com/view/8872c444b307e87101f69650.html</a> <div style="line-height:25px"> <div style="line-height:25px"><span style="line-height:25px"><span style="font-size:16px; line-height:28px">引言</span></span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">在用C++的项目源码中,经常会不可避免的会看到下面的代码:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#ifdef __cplusplus</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">extern "C" {</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#endif</span></div> <div style="line-height:25px"><span style="line-height:25px; color:rgb(51,102,255)"><span style="line-height:25px; white-space:pre"></span>/*...*/</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#ifdef __cplusplus</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">}</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#endif</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">它到底有什么用呢,你知道吗?而且这样的问题经常会出现在面试or笔试中。下面我就从以下几个方面来介绍它:</span></div> <div style="line-height:25px"><span style="line-height:25px"><span style="font-size:16px; line-height:28px">一、#ifdef _cplusplus/#endif _cplusplus及发散</span></span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">在介绍extern "C"之前,我们来看下</span><span style="color:#0000ff; line-height:25px">#ifdef _cplusplus/#endif _cplusplus</span><span style="color:#003366; line-height:25px">的作用。很明显</span><span style="color:#0000ff; line-height:25px">#ifdef/#endif、#ifndef/#endif</span><span style="color:#003366; line-height:25px">用于条件编译,</span><span style="color:#0000ff; line-height:25px">#ifdef _cplusplus/#endif _cplusplus</span><span style="color:#003366; line-height:25px">——表示如果定义了宏_cplusplus,就执行#ifdef/#endif之间的语句,否则就不执行。</span> </div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">在这里为什么需要#ifdef _cplusplus/#endif _cplusplus呢?因为C语言中不支持extern "C"声明,如果你明白extern "C"的作用就知道在C中也没有必要这样做,这就是条件编译的作用!在.c文件中包含了extern "C"时会出现编译时错误。</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">既然说到了条件编译,我就介绍它的一个重要应用——避免重复包含头文件。下面是一个开源web服务器——Mongoose的头文件mongoose.h中的一段代码:</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#ifndef MONGOOSE_HEADER_INCLUDED</span></div> <div style="line-height:25px"> <span style="color:#0000ff; line-height:25px">#define MONGOOSE_HEADER_INCLUDED</span><span style="line-height:25px; color:rgb(51,102,255); white-space:pre"> </span><span style="color:#3366ff; line-height:25px"></span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"> #ifdef __cplusplus</span></div> <div style="line-height:25px"> <span style="line-height:25px; color:rgb(51,102,255); white-space:pre"></span><span style="color:#ff6600; line-height:25px">extern "C"</span><span style="color:#3366ff; line-height:25px">{</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>#endif /* __cplusplus */</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span></span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>/*.................................</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>* do something here</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>*.................................</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>*/</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span></span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>#ifdef __cplusplus</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>}</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span>#endif /* __cplusplus */</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#endif /* MONGOOSE_HEADER_INCLUDED */</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">这个头文件mongoose.h可能在项目中被多个源文件包含(#include "mongoose.h"),而对于一个大型项目来说,这些冗余可能导致错误,因为一个头文件包含类定义或inline函数,在一个源文件中mongoose.h可能会被#include两次(如,a.h头文件包含了mongoose.h,而在b.c文件中#include a.h和mongoose.h)——这就会出错(在同一个源文件中一个结构体、类等被定义了两次)。</span><span style="line-height:25px; color:rgb(0,51,102)">从逻辑观点和减少编译时间上,都要求去除这些冗余。然而让程序员去分析和去掉这些冗余,不仅枯燥且不太实际,最重要的是有时候又需要这种冗余来保证各个模块的独立。</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">为了解决这个问题,上面代码中的</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#ifndef MONGOOSE_HEADER_INCLUDED</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">#define MONGOOSE_HEADER_INCLUDED</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">/*……………………………*/</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px">#endif /* MONGOOSE_HEADER_INCLUDED */</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">就起作用了。如果定义了MONGOOSE_HEADER_INCLUDED,#ifndef/#endif之间的内容就被忽略掉。因此,编译时第一次看到mongoose.h头文件,它的内容会被读取且给定MONGOOSE_HEADER_INCLUDED一个值。之后再次看到mongoose.h头文件时,MONGOOSE_HEADER_INCLUDED就已经定义了,mongoose.h的内容就不会再次被读取了。</span></div> <div style="line-height:25px"><span style="line-height:25px"><span style="font-size:16px; line-height:28px">二、extern "C"</span></span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">首先从字面上分析extern "C",它由两部分组成——extern关键字、"C"。下面我就从这两个方面来解读extern "C"的含义。</span></div> <div style="line-height:25px"><span style="line-height:25px">2.1、extern关键字</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">在一个项目中必须保证函数、变量、枚举等在所有的源文件中保持一致,除非你指定定义为局部的。首先来一个例子:</span></div> <div style="line-height:25px"> <span style="line-height:25px; white-space:pre"></span>file1.c文件</div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int x=1;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int f(){do something here}</span></div> <div style="line-height:25px"> <span style="line-height:25px; white-space:pre"></span>file2.c文件</div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> extern int x;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int f();</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> void g(){x=f();}</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">在file2.c中g()使用的x和f()是定义在file1.c中的。extern关键字表明file2.c中x,仅仅是一个变量的声明,其并不是在定义变量x,并未为x分配内存空间。变量x在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。但是可以声明多次,且声明必须保证类型一致,如</span>:</div> <div style="line-height:25px"> <span style="line-height:25px; white-space:pre"></span>file1.c文件</div> <div style="line-height:25px"> <span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span></span><span style="color:#0000ff; line-height:25px">int x=1;</span> </div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int b=1;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> extern c;</span></div> <div style="line-height:25px"> <span style="line-height:25px; white-space:pre"></span>file2.c文件</div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int x;// x equals to default of int type 0</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> int f();</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> extern double b;</span></div> <div style="line-height:25px"><span style="color:#3366ff; line-height:25px"><span style="line-height:25px; white-space:pre"></span> extern int c;</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">在这段代码中存在着这样的三个错误</span>:</div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">x被定义了两次</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">b两次被声明为不同的类型</span></div> <div style="line-height:25px"><span style="color:#0000ff; line-height:25px">c被声明了两次,但却没有定义</span></div> <div style="line-height:25px"> <span style="color:#000080; line-height:25px">在C/C++语言中,</span><span style="color:#ff6600; line-height:25px">extern关键字</span><span style="color:#000080; line-height:25px">告诉编译器</span><span style="color:#ff6600; line-height:25px">在所有的模块中(不仅仅是本地模块)查找该函数和全局变量的定义。</span><span style="color:#000080; line-height:25px">通常,</span><span style="color:#ff6600; line-height:25px">在模块的头文件中,对于本模块提供给其它模块引用的函数和全局变量应以关键字extern声明</span><span style="color:#000080; line-height:25px">。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;在连接阶段,连接器会在所有模块找查找该函数的定义,这里它将在模块A中找到了该函数的定义。</span> </div> <div style="line-height:25px"><span style="color:#ff6600; line-height:25px">与extern对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。</span></div> <div style="line-height:25px"><span style="line-height:25px">2.2、"C"</span></div> <div style="line-height:25px"><span style="color:#003366; line-height:25px">一个C++程序包含其它语言编写的部分代码,同理,C++编写的代码片段可能被使用在其它语言编写的代码中。不同语言编写的代码互相调用是困难的,即使是同一种编写的代码但不同的编译器编译的代码。例如,不同语言和同种语言的不同实现可能会在注册变量保持参数和参数在栈上的布局,这个方面不一样。</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">为了使它们遵守统一规则,可以使用extern指定一个编译和连接规约。例如,声明C和C++标准库函数strcyp(),并指定它应该根据C的编译和连接规约来链接:</span></div> <div style="line-height:25px"> <span style="color:#ff6600; line-height:25px">extern "C"</span><span style="color:#0000ff; line-height:25px">char* strcpy(char*,const char*);</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">注意它与下面的声明的不同之处:</span></div> <div style="line-height:25px"> <span style="color:#ff6600; line-height:25px">extern</span><span style="color:#0000ff; line-height:25px">char* strcpy(char*,const char*);</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">下面的这个声明仅表示在连接的时候调用strcpy()。</span></div> <div style="line-height:25px"> <span style="color:#ff6600; line-height:25px">extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。extern "C"指令仅指定编译和连接规约,但不影响语义</span>。<span style="color:#000080; line-height:25px">例如在函数声明中,指定了extern "C",仍然要遵守C++的类型检测、参数转换规则。</span> </div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">再看下面的一个例子,为了声明一个变量而不是定义一个变量,你必须在声明时指定extern关键字,但是当你又加上了"C",它不会改变语义,但是会改变它的编译和连接方式。</span></div> <div style="line-height:25px"><span style="color:#000080; line-height:25px">如果你有很多语言要加上extern "C",你可以将它们放到extern "C"{ }中。</span></div> <div style="line-height:25px"><span style="line-height:25px">2.3、小结extern "C"</span></div> <div style="line-height:25px"> <span style="color:#003366; line-height:25px">通过上面两节的分析,我们知道extern "C"的真实目的是实现类C和C++的混合编程。在C++源文件中的语句前面加上extern "C",表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。这样在</span><span style="color:#0000ff; line-height:25px">类C</span><span style="color:#003366; line-height:25px">的代码中就可以调用C++的函数or变量等</span>。(<span style="line-height:25px">注</span>:<span style="color:#000080; line-height:25px">我在这里所说的类C,代表的是跟C语言的编译和连接方式一致的所有语言</span>)</div> </div> </wbr>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值