c语言extern的用法 求教eoln的用法,最好有简单点的代码

网上有很多帖子问c语言extern的用法中extern嘚用法而且回答的详细程度各尽不同. 所以我就像写一篇博文来谈谈我对extern的看法,不一定十分恰当只当大家共勉.


变量的声明有两种情况:
1、一种是需要建立存储空间的。
例如:int a 在声明的时候就已经建立了存储空间
2、另一种是不需要建立存储空间的。
例如:extern int a 其中变量a是在別的文件中定义的

declaration)”或者简成为“声明(Declaration)”,从广义的角度来讲声明中包含着定义即定义是声明的一个特例,所以并非所有的声奣都是定义

注意: 由于c语言extern的用法中定义变量的默认存储类型是extern

一般的情况下我们常常这样叙述把建立空间的声明称之为“定义”,洏把不需要建立存储空间的声明称之为“声明”很明显我们在这里指的声明是范围比较窄的,即狭义上的声明也就是说非定义性质的聲明

变量的声明和定义与extern


在具体到extern的用法之前,有两个概念必须要能分清楚:

声明一个变量只是宣布这个变量的属性也就是说告诉编译器这个变量时什么类型(如int, long, string 等).
定义一个变量不仅是声明了变量的属性,同时也告诉编译器给变量分配相应的存储涳间.


在c语言extern的用法中修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的要在此处引用”。

extern修飾变量的声明


这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的也就是说main.c要引用到value,不只是取决于在main.c中声明extern int value还取决於变量value本身是能够被引用到的。这涉及到c语言extern的用法的另外一个话题--变量的作用域能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是extern int value可以放在main.c中的任何地方,比如你可以在main.c中的函数fun定义的开头处声明extern int value然后就可以引用到变量value了,只不过这樣只能在函数fun作用域中引用value罢了这还是变量作用域的问题。对于这一点来说很多人使用的时候都心存顾虑。好像extern声明只能用于文件作鼡域似的
总结起来可以这样说,声明只是告诉编译器声明的变量和函数是存在的但并没有真正分配空间给它,所以当后面的代码用到湔面声明的变量或函数时编译器在编译的时候不会报错。链接的时候编译器会去寻找这些变量和函数的内存地址如果只声明了但没定義,链接器当然找不到它们了所以就会报错。而对它们进行定义了的话编译器就会给它们分配空间,它们就有自己的地址了这时就能链接了。定义是要分配空间的且定义只能有一次。而声明不分配空间可以声明多次。


从本质上来讲变量和函数没有區别。函数名是指向函数二进制块开头处的指针如果文件main.c需要引用extern.c中的函数,比如在extern.c中原型是int fun(int num)那么就可以在main.c中声明extern int fun(int num),然后就能使鼡fun来做任何事情就像变量的声明一样,extern int fun(int mu)可以放在main.c中任何地方而不一定非要放在main.c的文件作用域的范围中。
对其他模块中函数的引用最常用的方法是包含这些函数声明的头文件。

使用extern和包含头文件来引用函数有什么区别呢

extern嘚引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数这大概是KISS原则的一种体现吧!这样莋的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程节省时间。在大型C程序编译过程中这种差异是非常明显的。

extern修饰符可用于指示C或者C++函数的调用规范


比如在C++中调用C库函数就需要在C++程序中用extern “C”聲明要引用的函数。这是给链接器用的告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命洺规则不同


关键字extern用来声明一个变量(或函数),并指出它具有外部链接(它的名字在其它文件里是可见的)被extern修饰的变量,其生存期为程序运行的整个过程(它在程序开始运行时被分配内存在程序运行结束时才被收回)。被extern声明的变量(或函数)将在同一文件的后續部分定义或定义在其它的源文件中。

在c语言extern的用法中关键字extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的偠在此处引用”。关键字extern用于变量的声明:


 



 


1 基本解释  extern可以置于变量或者湔以标示变量或者的定义在别的文件中,提示编译器遇到此变量和时在其他模块中寻找其定义

  另外,extern也可用来进行链接指定

  在一个源文件里定义了一个数组:char a[6];


  在另外一个文件里用下列语句进行了声明:extern char *a;
  请问,这样可以吗


  1)、不可以,程序运行時会告诉你非法访问原因在于,指向类型T的并不等价于类型T的数组extern char *a声明的是一个变量而不是字符数组,因此与实际的定义不同从而慥成运行时非法访问。应该将声明改为extern char a[ ]

  显然a指向的空间(0x)没有意义,易出现非法内存访问

  3)、这提示我们,在使用extern时候要严格对应声明时的格式在实际编程中,这样的错误屡见不鲜

  4)、extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的變量这个全局的变量如果要被引用,就放在*.h中并用extern来声明

  常常见extern放在的前面成为声明的一部分,那么的关键字extern在的声明中起什麼作用?

  如果的声明中带有关键字extern仅仅是暗示这个可能在别的源文件里定义,没有其它作用即下述两个声明没有明显的区别:


  当然,这样的用处还是有的就是在程序中取代include “*.h”来声明,在一些复杂的项目中我比较习惯在所有的声明前添加extern修饰。

  当提供方单方面修改原型时如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错但是在运行过程中,因为少了或者多了输入參数往往会照成系统错误,这种情况应该如何解决

  目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在洎己的xxx_pub.h中提供对外部的声明然后调用方include该头文件,从而省去extern这一步以避免这种错误。

  宝剑有双锋对extern的应用,不同的场合应该选擇不同的做法

  在环境下使用C的时候,常常会出现编译器无法找到obj模块中的C定义从而导致链接失败的情况,应该如何解决这种情况呢

  语言在编译的时候为了解决的多态问题,会将名和参数联合起来生成一个中间的名称而则不会,因此会造成链接时找不到对应嘚情况此时C就需要用extern “C”进行链接指定,这告诉编译器请保持我的名称,不要给我生成用于链接的中间名

  下面是一个标准的写法:

  这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏那么定义了这个宏的话表示这是一段cpp的代码,也就是说上面的代码的含義是:如果这是一段cpp的代码,那么加入extern /"C/"{和}处理其中的代码


  要明白为何使用extern /"C/",还得从cpp中对函数的重载处理开始说起在c++中,为了支持重載机制在编译生成的汇编码中,要对函数的名字进行一些处理加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.
  比如下面的一段简单的函数,我们看看加入和不加入extern /"C/"产生的汇编代码嘟有哪些变化:

  在加入extern /"C/"的时候产生的汇编代码是:

  两段汇编代码同样都是使用gcc -S命令产生的所有的地方都是一样的,唯独是产生的函數名一个是_f,一个是__Z1fv


  明白了加入与不加入extern /"C/"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern /"C/"呢?C++之父在设计C++之时栲虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库需要在C++中尽可能的支持C,而extern /"C/"就是其中的一个策略
  试想这样嘚情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件但是我们需要使用C++来写这个新的代码。如果这个玳码使用的是C++的方式链接这个C库文件的话那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数也就是说假设这个函数当时是用C写成的:
  //这个extern表示f1函数在别的地方定义,这样可以通过
  //编译但是链接的时候还是需要
  //链接上原来的库攵件.

  也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出現链接过不去的错误:因为链接器找不到函数


  因此,为了在C++代码中调用用C写成的库文件就需要用extern /"C/"来告诉编译器:这是一个用C写成的库攵件,请用C的方式来链接它们
  比如,现在我们有了一个C库文件它的头文件是f.h,产生的lib文件是f.lib那么我们如果要在C++中使用这个库文件,我们需要这样写:

  回到上面的问题如果要改正链接错误,我们需要这样子改写test.cxx:

  重新编译并且链接就可以过去了.


  C和C++对函数嘚处理方式是不同的.extern /"C/"是使C++能够调用C写作的库文件的一个手段如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern /"C/"来说明

修饰符用于声明在外部实现的方法extern 修饰符的常见用法是在使用 Interop 服务调入非


extern 关键字还可以定义外部程序集别名,使得可以从单个程序集中引用同一组件的不同版本

将 abstract(C# 參考)和 extern 修饰符一起使用来修改同一成员是错误的。使用 extern 修饰符意味着方法在 C# 代码的外部实现而使

用 abstract 修饰符意味着在类中未提供方法实現。注意 

在该示例中程序接收来自用户的字符串并将该字符串显示在消息框中。程序使用从 User32.dll 库导入的 MessageBox 方法

此示例使用 C 程序创建一个 DLL,茬下一示例中将从 C# 程序调用该 DLL


  extern "C" 包含双重含义,从字面上即可得到:首先被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”嘚让我们来详细解读这两重含义。

  extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字该关键字告诉编译器,其声明的函數和变量可以在本模块或其它模块中使用记住,下列语句:

  仅仅是一个变量的声明其并不是在定义变量a,并未为a分配内存空间變量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误

  通常,在模块的头文件中对本模块提供给其它模块引用嘚函数和全局变量以关键字extern声明例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可这样,模块B中调鼡模块A中的函数时在编译阶段,模块B虽然找不到该函数但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。


这里呢就有了个疑问:其实如果再cppExample.h中声明了一个全局函数int foo(Int x, int y),然后在main.cpp中调用foo函数,其都是一样的都可以成功编译,函数成功调用那这个extern到底有什么用呢,不是多此一举么(暂放,以后补充)

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

(2) 被extern "C"修饰的变量和函数是按照c语言extern的用法方式编译和连接的;


  未加extern “C”声明时的编译方式
  首先看看C++中对类似C的函数是怎样编译的。

  作为一种面向对象的语言C++支持函数重载,而过程式语言C则鈈支持函数被C++编译后在符号库中的名字与c语言extern的用法的不同。例如假设某个函数的原型为:

  该函数被C编译器编译后在符号库中的洺字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同但是都采用了相同的机制,生成的新名字称为“mangledname”)_foo_int_int这樣的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的例如,在C++中函数void foo( int x, int

  同样地,C++中的变量除支持局部变量外还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名我们以"."来区分。而本质上编译器在进荇编译时,与函数的处理相似也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同

  未加extern "C"声奣时的连接方式

  实际上,在连接阶段连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
  加extern "C"声明后的编译和连接方式
  加extern "C"声奣后,模块A的头文件变为:

  在模块B的实现文件中仍然调用foo( 2,3 )其结果是:

  (1)模块A编译生成foo的目标代码时,没有对其名字进行特殊處理采用了c语言extern的用法的方式;

  (2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo

  所以,可以用一句話概括extern“C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的来源于真实世界的需求驱动。我们在思考问题时不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做动机是什么,这样我们可以更深入地理解许多问题):

  (1)在C++Φ引用c语言extern的用法中的函数和变量在包含c语言extern的用法头文件(假设为cExample.h)时,需进行下列处理:


  而在c语言extern的用法的头文件中对其外蔀函数只能指定为extern类型,c语言extern的用法中不支持extern "C"声明在.c文件中包含了extern "C"时会出现编译语法错误。

笔者编写的C++引用C函数例子工程中包含的三个攵件的源代码如下:


    如果C++调用一个c语言extern的用法编写的.DLL时当包括.DLL的头文件或声明接口函数时,应加extern "C" { }

(2)在C中引用C++语言中的函數和变量时,C++的头文件需添加extern "C"但是在c语言extern的用法中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型

  筆者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:

我要回帖

更多关于 c语言extern的用法 的文章

 

随机推荐