C/C++ extern关键字

C/C++ extern关键字

本篇文章主要围绕一下几个问题对extern关键字进行说明。

  • 声明和定义
  • 变量和函数的声明
  • extern和static
  • extern关键字与头文件的联系
  • extern “C”
  • extern和const

声明和定义

变量声明:用于向程序表明变量的类型和名字。变量可以被声明多次
变量定义:用于为变量分配存储空间,还可为变量指定初始值。变量只能被定义一次。定义也是声明:当定义变量的时候我们声明了它的类型和名字。

 extern表示声明,可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。

 通过上面的两句话我们很容易搞清楚这两个概念,但是有的时候即使搞清楚了他们之间的区别和联系,可是在真正编码时又会产生很多疑问。

 首先我们知道变量的声明是需要extern关键字的,在声明变量的时候,这个extern必须添加在变量前。其次,定义变量的同时我们也声明了变量,但是extern关键字在定义变量的时候可以被省略(定义时,默认均省略),所以有时会让你搞不清楚到底是声明还是定义。

变量和函数的声明

我们一定要牢记一句话,程序中的变量可以声明多次,但是只能定义一次。

extern int a; // 声明一个全局变量 a
int a; // 定义一个全局变量 a
int a = 0;    // 定义一个全局变量 a, 并给初值,
extern int a = 0 ; // 定义一个全局变量 a 并给初值。此时,该语句相当于 int a = 0;

当你要引用一个全局变量的时候,你就必须要声明,extern int a; 这时候extern不能省略,因为省略了,就变成int a;这是一个定义,不是声明。

对于函数同样也是定义和声明,定义的时候用extern,说明这个函数是可以被外部引用的,声明的时候用extern说明这是一个声明。 但由于函数的定义和声明是有区别的,定义函数要有函数体,声明函数没有函数体(还有以分号结尾),所以函数定义和声明时都可以将extern省略掉,反正其他文件也是知道这个函数是在其他地方定义的,所以不加extern也行。两者如此不同,所以省略了extern也不会有问题。

/*.cpp文件*/

//定义一个函数
int fun(void)
{
      return 0;
}
/*.h文件*/

//声明一个函数
extern int fun(void);//声明一个函数可以省略extern

extern和static

(1)extern表明声明一个变量,变量的定义在别的地方,在这里要使用那个变量。

(2)static 表示静态的变量,分配内存的时候,存储在静态区,不存储在栈上面。

static作用范围是内部连接的关系这和extern有点相反。它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern引用,而static不可以,只允许对象本身用它。具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它。

extern关键字与头文件的联系

首先说下头文件,其实头文件对计算机而言没什么作用,她只是在预编译时在 #include 的地方展开一下,没别的意义了,其实头文件主要是给别人看的。头文件就是对用户的说明,函数,参数,各种各样的接口的说明。所以,最好不要在头文件里定义什么东西。比如全局变量:

/*.h文件*/
int A;

这里的 int A 是个全局变量的定义,所以如果这个头文件被多次引用的话,你的A会被重复定义,显示语法上错了。

程序模块化设计风格

  1. 不要把变量定义放入.h文件,这样容易导致重复定义错误。
    永远不要在.h文件中定义变量。定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量,所以我们在.h文件中一定要写声明,而不是定义。
  2. 尽量使用static关键字把变量定义限制于该源文件作用域,除非变量被设计成全局的。
  3. 可以在头文件中声明一个变量,在用的时候包含这个头文件就声明了这个变量。

模块化要点

  1. 模块即是一个.c文件和一个.h文件的结合,头文件(.h)中是对于该模块接口的声明。
  2. 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明,在.c文件中对变量和函数进行定义。在别的文件中使用全局变量和全局函数的时候,只需要在该文件中引入.h文件即可。
  3. 模块内的函数和全局变量需在.c文件开头冠以static关键字声明。
  4. 永远不要在.h文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。

注意:不过有三个例外,一下三中实体的定义也可放到头文件中。

1.值在编译时就已知的const变量的定义可以放到头文件中
如:const int num(10);
2.类的定义可以放到头文件中
3.inline 函数

extern “C”

在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?
答案与分析
  C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
下面是一个标准的写法:

//在.h文件的头上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
 #endif
 #endif /* __cplusplus */
 …
 …
 //.h文件结束的地方
 #ifdef __cplusplus
 #if __cplusplus
}
#endif
#endif /* __cplusplus */ 

extern和const

C++中const修饰的全局常量具有跟static相同的特性,即它们只能作用于本编译模块中,且static修饰的是全局变量,但是const可以与extern连用来声明该常量可以作用于其他编译模块中,如: extern const char g_str[];

然后在原文件中别忘了定义:const char g_str[] = “123456”;

所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,需要注意的是,const char* g_str = “123456” 与 const char g_str[] =”123465”是不同的,前面那个const修饰的是char 而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char g_str遵守const的全局常量的规则,最好这么定义const char* const g_str=”123456”。
有关const关键字的知识,请参考C/C++ const关键字

参考资料

C/C++中extern关键字详解
extern 与头文件(*.h)的区别和联系
深入理解extern用法
C语言中声明和定义详解


   转载规则


《C/C++ extern关键字》 anhua 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
C++模板 C++模板
C++模板模板是泛型编程的基础,泛型编程,即以一种独立于任何特定类型的方式编码。模板是创建泛型类或函数的蓝图或公式。C++中使用template关键字来定义模板。 在理解泛型之前,首先我们需要了解什么时泛型编程,简单点来说,因为C++是
2019-12-19
下一篇 
c/c++ const关键字 c/c++ const关键字
C/C++ const关键字  在C语言和C++中经常要用到const关键字,最简单的用法是使用它定义常量,如果一个变量被const修饰,那么它的值就不能再被改变。这是const关键字最根本的功能。既然用它定义的变量不可变,那么他与#def
2019-12-15
  目录