
在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。
1 extern修饰变量的声明。举例来说,如果文件ac需要引用bc中变量int v,就可以在ac中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说ac要引用到v,不只是取决于在ac中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在ac中的任何地方,比如你可以在ac中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。
2 extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件ac需要引用bc中的函数,比如在bc中原型是int fun(int mu),那么就可以在ac中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在ac中任何地方,而不一定非要放在ac的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
extern "C"的含义
extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。
被extern "C"限定的函数或变量是extern类型的;
被extern "C"修饰的变量和函数是按照C语言方式编译和链接的
首先看看C++中对类似C的函数是怎样编译的。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。
_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。 例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以""来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
举例说明
(1)未加extern "C"声明时的连接方式
假设在C++中,模块A的头文件如下:
// 模块A头文件 moduleAh
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif
//在模块B中引用该函数:
// 模块B实现文件 moduleBcpp
#include "moduleAh"
foo(2,3);
实际上,在连接阶段,链接器会从模块A生成的目标文件moduleAobj中寻找_foo_int_int这样的符号!
(2)加extern "C"声明后的编译和链接方式
加extern "C"声明后,模块A的头文件变为:
// 模块A头文件 moduleAh
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo( int x, int y );
#endif
在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:
<1>A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
<2>链接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。
如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo(int x, int y),则模块B找不到模块A中的函数;反之亦然。
extern “C”这个声明的真实目的是为了实现C++与C及其它语言的混合编程。
函数如果定义在本c文件中, 或者定义在一个h文件中并且本c文件#include了这个h就不用加extern,因为这两种情况下本c文件都可以直接找到
一旦有一个函数定义在另一个c文件中,而你要想在本c文件中使用的时候就必须用extern关健字声明一下,否则链接会出错。 extern的作用就是告诉链接器,在这个工程中的某一个obj文件中有extern修饰的后边这函数,这样链接器就可以正常工作了。否则链接器在本obj文件中找不到你后边写的这个函数符号,就会报错
extern关键字可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。这里起到的是声明作用范围的用处。
extern的另外用法是当C和C++混合编程时假如c++调用的是c源文档定义的函数或变量,那么要加extern来告诉编译器用c方式命名函数:
文档Acpp调用ac里面的变量i和函数callme()
1、可以通过把原函数名与参数信息结合,产生一个独特的内部名字,这种技术叫做名字修饰(Name Mangling)。名字修饰规则没有一个标准,所以不同的编译器的名字修饰规则也不一样。
下面是一组函数,其中f()函数重载了:
f(void)和f(int)是不同的函数,除了函数名相同以外没有任何关系。当生成obj目标文件时,为了区分它们,C++编译器根据参数信息进行了名字修饰:
2、C语言中没有名字修饰(Name Mangling),因为C语言不支持函数重载。但是如果C++中含有C代码,在编译时C++编译器对C代码的函数也会进行名字修饰,函数名变了以后,将导致在C运行库中找不到对应函数,发生链接错误。
为了防止C++编译器对C代码进行名字修饰,我们将C代码用extern “C”进行链接指定,告诉编译器,在生成中间文件时,不要对这部分代码进行名字修饰,而是生成符合C规则的中间符号名。
添加了extern “C”链接指示后,上面的代码就能够正常运行了。
extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
(1) 被extern "C"限定的函数或变量是extern类型的
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:
extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
(2) 被extern "C"修饰的变量和函数是按照C语言方式编译和连接的
extern int i;//写成i = 10;行吗?-- 在函数体内赋值是可以的。
extern void fun(void);//两个void 可否省略?-- 不可省略。两个void是用来说明外部函数的返回值和参数的。
extern double j;//这样行吗?为什么?-- 不可以。C程序中,不允许出现类型不同的同名变量。
j = 30;//这样行吗?为什么?-- 可以。但是数值会被取整。
C中,extern的作用是声明一个位于其他程序文件内的全局变量,以便在本程序文件中正确使用该变量。就是说,变量已经在别的文件里声明过了,那么在其他的文件里只要在同样的变量声明语句前用extern加以修饰,就可以在程序里如同一个普通变量一样使用了。
个人看法,仅供参考。
(1)别的就是指“这一个”以外的;C盘定义,D盘调用,原则上可以,但是麻烦,写程序应该越简单越好,不提倡这样做;“别的”原则上没有限定几层,但是最好放在同一个文件夹,原因也说了,分开放麻烦。
(2)作用相同,不完全等价。区别在哪呢?include “h”
int f();把“h”里的所有东西都搬过来(除了f()之外可能还有其他的),那么肯定包含有f();而extern int f();表示你明确知道你要调用的是“h”里的f()这个函数,其它你都不需要。
“这么说,如果所有外部函数声明时都加上extern就不需要include “h”头文件了?”是的,但是你想想,如果你所有外部函数都要用到的话,你自己手动给所有函数加上extern还是直接一个include “h”哪个更方便呢
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)