extern 头文件 定义&声明

 https://www.cnblogs.com/tshua/p/5741009.htmlhtml

用#include能够包含其余头文件中变量、函数的声明,为何还要extern关键字?程序员

       若是我想引用一个全局变量或函数a,我只要直接在源文件中包含#include<xxx.h> (xxx.h包含了a的声明)不就能够了么,为何还要用extern呢??函数

       这个问题一直也是似是而非的困扰着我许久,通过实践和查找资料,有以下总结:spa

1、头文件code

        首先说下头文件,其实头文件对计算机而言没什么做用,她只是在预编译时在#include的地方展开一下,没别的意义了,其实头文件主要是给别人看的。htm

        我作过一个实验,将头文件的后缀改为xxx.txt,而后在引用该头文件的地方用对象

   #include"xxx.txt"blog

        编译,连接都很顺利的过去了,由此可知,头文件仅仅为阅读代码做用,没其余的做用了!接口

        无论是C仍是C++,你把你的函数,变量或者结构体,类啥的放在你的.c或者.cpp文件里。而后编译成lib,dll,obj,.o等等,而后别人用的 时候,最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。
        但对于咱们程序员而言,他们怎么知道你的lib,dll...里面到底有什么东西?要看你的头文件。你的头文件就是对用户的说明。函数,参数,各类各样的接口的说明。
        那既然是说明,那么头文件里面放的天然就是关于函数,变量,类的“声明”(对函数来讲,也叫函数原型)了。记着,是“声明”,不是“定义”。ip

      声明和定义的区别。

复制代码
    变量的声明有两种状况:

    一、一种是须要创建存储空间的。例如:int a 在声明的时候就已经创建了存储空间。

    二、另外一种是不须要创建存储空间的。 例如:extern int a 其中变量a是在别的文件中定义的。

    声明是向编译器介绍名字--标识符。它告诉编译器“这个函数或变量在某处可找到,它的模样象什么”。

    而定义是说:“在这里创建变量”或“在这里创建函数”。它为名字分配存储空间。不管定义的是函数仍是变量,编译器都要为它们在定义点分配存储空间。 对于变量,编译器肯定变量的大小,而后在内存中开辟空间来保存其数据,对于函数,编译器会生成代码,这些代码最终也要占用必定的内存。

    总之就是:把创建空间的声明成为“定义”,把不须要创建存储空间的成为“声明”。

    基本类型变量的声明和定义(初始化)是同时产生的;而对于对象来讲,声明和定义是分开的。

    例如:类A

    若是A a;就是一个声明,告诉编译器a是A类的一个对象变量,可是不进行初始化;

    若是之后a=new A();这就是初始化,分配了空间。

    (咱们声明的最终目的是为了提早使用,即在定义以前使用,若是不须要提早使用就没有单独声明的必要,变量是如此,函数也是如此,因此声明不会分配存储空间,只有定义时才会分配存储空间。)

    用static来声明一个变量的做用有二:

    (1)对于局部变量用static声明,则是为该变量分配的空间在整个程序的执行期内都始终存在。

    (2)外部变量用static来声明,则该变量的做用只限于本文件模块。


    补充:

    什么是定义?什么是声明?它们之间的区别是什么?

    所谓定义就是(编译器)建立一个对象,为这个对象分配一块内存,并给它取上一个名字,这个名字就是就是咱们常常所说的变量名或对象名。

    声明有2重含义:

    (1) 告诉编译器,这个名字已经匹配到一块内存上,下面的代码用到变量或者对象是在别的地方定义的。声明能够出现屡次。

    (2) 告诉编译器,这个名字已经被预约了,别的地方不再能用它来做为变量名或对象名。

    定义和声明的最重要区别就是:

    定义建立对象并为这个对象分配了内存,声明没有分配内存。
复制代码

 

 

因此,最好不要傻嘻嘻的在头文件里定义什么东西。好比全局变量:
  /*xx头文件*/
  #ifndef _XX_头文件.H
  #define _XX_头文件.H
  int A;
  #endif

        那么,很糟糕的是,这里的int A是个全局变量的定义,因此若是这个头文件被屡次引用的话,你的A会被重复定义,显然语法上错了。只不过有了这个#ifndef的条件编译,因此能保证你的头文件只被引用一次,不过也许仍是不会出岔子,但若多个c文件包含这个头文件时仍是会出错的,由于宏名有效范围仅限于本c源文件,因此在这多个c文件编译时是不会出错的,但在连接时就会报错,说你多处定义了同一个变量,

  Linking...
  incl2.obj : error LNK2005: "int glb" (?glb@@3HA) already defined in incl1.obj
  Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols found

  注意!!!

2、extern

        这个关键字真的比较可恶,在定义变量的时候,这个extern竟然能够被省略(定义时,默认均省略);在声明变量的时候,这个extern必须添加在变量前,因此有时会让你搞不清楚究竟是声明仍是定义。或者说,变量前有extern不必定就是声明,而变量前无extern就只能是定义。注:定义要为变量分配内存空间;而声明不须要为变量分配内存空间。

     下面分变量和函数两类来讲:

(1)变量

  尤为是对于变量来讲。
  extern int a;//声明一个全局变量a
  int a; //定义一个全局变量a

  extern int a =0 ;//定义一个全局变量a 并给初值。
  int a =0;//定义一个全局变量a,并给初值,

       第四个 等于 第 三个,都是定义一个能够被外部使用的全局变量,并给初值。
       糊涂了吧,他们看上去可真像。可是定义只能出如今一处。也就是说,无论是int a;仍是extern int a=0;仍是int a=0;都只能出现一次,而那个extern int a能够出现不少次。

       当你要引用一个全局变量的时候,你就必需要声明,extern int a; 这时候extern不能省略,由于省略了,就变成int a;这是一个定义,不是声明。注:extern int a; 中类型int可省略,即extern a; 但其余类型则不能省略。

 

(2)函数
       函数,对于函数也同样,也是定义和声明,定义的时候用extern,说明这个函数是能够被外部引用的,声明的时候用extern说明这是一个声明。 但因为函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体(还有以分号结尾),因此函数定义和声明时均可以将extern省略掉,反正其余文件也是知道这个函数是在其余地方定义的,因此不加extern也行。二者如此不一样,因此省略了extern也不会有问题。
   好比:
  /*某cpp文件*/
  int fun(void)
  {
        return 0;
  }

  很好,咱们定义了一个全局函数
  /*另外一cpp文件*/
  int fun(void);
  咱们对它作了个声明,而后后面就能够用了
  加不加extern都同样
  咱们也能够把对fun的声明 放在一个头文件里,最后变成这样
  /*fun.h*/
  int fun(void);   //函数声明,因此省略了extern,完整些是extern int fun(void);
  /*对应的fun.cpp文件*/
  int fun(void)
  {
       return 0;
  }//一个完整的全局函数定义,由于有函数体,extern一样被省略了。
       而后,一个客户,一个要使用你的fun的客户,把这个头文件包含进去,ok,一个全局的声明。没有问题。
可是,对应的,若是是这个客户要使用全局变量,那么要extern 某某变量;否则就成了定义了。

        总结:

        对变量而 言,若是你想在本源文件(例如文件名A)中使用另外一个源文件(例如文件名B)的变量,方法有2种:(1)在A文件中必须用extern声明在B文件中定义 的变量(固然是全局变量);(2)在A文件中添加B文件对应的头文件,固然这个头文件包含B文件中的变量声明,也即在这个头文件中必须用extern声明 该变量,不然,该变量又被定义一次。

       对函数而 言,若是你想在本源文件(例如文件名A)中使用另外一个源文件(例如文件名B)的函数,方法有2种:(1)在A文件中用extern声明在B文件中定义的函 数(其实,也可省略extern,只需在A文件中出现B文件定义函数原型便可);(2)在A文件中添加B文件对应的头文件,固然这个头文件包含B文件中的 函数原型,在头文件中函数能够不用加extern。

******************************************************************************************************************************************************

       对上述总结换一种说法:

         (a)对于一个文件中调用另外一个文件的全局变量,由于全局变量通常定义在原文件.c中,咱们不能用#include包含源文件而只能包含头文件,因此经常使用的方法是用extern  int a来声明外部变量。   另一种方法是能够是在a.c文件中定义了全局变量int global_num ,能够在对应的a.h头文件中写extern int global_num ,这样其余源文件能够经过include a.h来声明她是外部变量就能够了。

      (b)还有变量和函数的不一样举例

          int fun(); 和 extern int fun(); 都是声明(定义要有实现体)。  用extern int  fun()只是更明确指明是声明而已。

         而 int a;   是定义      

               extern int a; 是声明。

 

(3)此外,extern修饰符可用于C++程序中调用c函数的规范问题。

 好比在C++中调用C库函数,就须要在C++程序中用extern “C”声明要引用的函数。这是给连接器用的,告诉连接器在连接的时候用C函数规范来连接。主要缘由是C++和C程序编译完成后在目标代码中命名规则不一样。

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

 

3、extern和头文件的联系
        这种联系也解决了最初提出的2个问题:

  (a)用#include能够包含其余头文件中变量、函数的声明,为何还要extern关键字?

       (b)若是我想引用一个全局变量或函数a,我只要直接在源文件中包含#include<xxx.h> (xxx.h包含了a的声明)不就能够了么,为何还要用extern呢??

         答 案:若是一个文件(假设文件名A)要大量引用另外一个文件(假设文件名B)中定义的变量或函数,则使用头文件效率更高,程序结构也更规范。其余文件(例如文 件名C、D等)要引用文件名B中定义的变量或函数,则只需用#include包含文件B对应的头文件(固然,这个头文件只有对变量或函数的声明,毫不能有 定义)便可。

********************************************************************************************************************************************

       那是一个被遗忘的年代,那时,编译器只认识.c(或.cpp)文件,而不知道.h是何物的年代。       那时的人们写了不少的.c(或.cpp)文件,渐渐地,人们发如今不少.c(或.cpp)文件中的声明变量或函数原型是相同的,但他们却不得不一个字一个 字地重复地将这些内容敲入每一个.c(或.cpp)文件。但更为恐怖的是,当其中一个声明有变动时,就须要检查全部的.c(或.cpp)文件,并修改其中的 声明,啊~,简直是世界末日降临!       终于,有人(或许是一些人)再不能忍受这样的折磨,他(们)将重复的部分提取出来,放在一个新文件里,而后在须要的.c(或.cpp)文件中敲 入#include   XXXX这样的语句。这样即便某个声明发生了变动,也再不须要处处寻找与修改了---世界仍是那么美好!       由于这个新文件,常常被放在.c(或.cpp)文件的头部,因此就给它起名叫作“头文件”,扩展名是.h.       今后,编译器(实际上是其中预处理器)就知道世上除了.c(或.cpp)文件,还有个.h的文件,以及一个叫作#include命令。

相关文章
相关标签/搜索