在实际开发C++项目的时候,一个项目里面往往会包含多个cpp文件,多个头文件,有时还会有c文件。由于变量的声明可以有多次,但定义只能有一次,这就不可避免地要使用extern,static关键字。弄清楚这几个关键字之间的用法和区别,能节省很多调试项目的时间,尤其是对于一些难以定位的链接错误。下面分别介绍:
一、extern关键字
这个关键字可能是最容易用错的一个,一方面可能是因为平时写的小程序不太会用到这个关键字,另一方则是这个关键字确实有些不太容易理解的用法。
extern关键字主要有两个作用:1、声明一个全局变量;2、extern “C” { /*用C实现的内容(通常写在另外的.c文件中)*/ }
1、当用extern声明一个全局变量的时候,首先应明确一点:extern的作用范围是整个工程,也就是说当我们在.h文件中写了extern int a;链接的时候链接器会去其他的.cpp文件中找有没有int a的定义,如果没有,链接报错;当extern int a;写在.cpp文件中时,链接器会在这个.cpp文件该声明语句之后找有没有int a的定义,然后去其他的.cpp文件中找,如果都找不到,链接报错。另外值得注意的一点是,当extern语句出现在头文件中时,不要将声明和定义在一条语句中给出,也就是不要在头文件中写类似于这样的语句:
extern int a = 1;
这种写法,在g++编译时会给出一个警告:warning: 'a' initialized and declared 'extern'。
这样做的一个可能的原因是:担心在其他的.cpp文件中忘记给出该变量的定义,所以干脆就在声明的时候直接给出定义得了,省得链接时候报错说变量a没有定义。
对变量没有定义的忧虑是应该提倡的,但解决这种忧虑的办法却弄巧成拙了。试想以下程序结构:
头文件A.h
extern int a = 1;
头文件B.h
#include "A.h"
源文件A.cc
#include "A.h"这样一来,编译A.cc时,会报错说变量a重复定义。
#include "B.h"
int main(){
int t = a;
return 0;
}
当我,我们可以通过在头文件A.h中加上头文件保护符来解决这个编译错误,但将全局变量的定义放在头文件中总归是不好的。
遵照的法则其实很简单:只在头文件中通过extern给出全局变量的声明,在源文件中给出定义(并且只能定义一次)。
2、C++完全兼容C,当extern与“C”连用时,作用是告诉编译器用C的编译规则去解析extern “C”后面的内容。最常见的差别就是C++支持函数重载,而标准C是不支持的。如果不指明extern “C”,C++编译器会根据自己的规则在编译函数时为函数名加上特定的后缀以区别不同的重载版本,而如果是按C的标准来编译的话,则不需要。
二、static关键字
static关键字的作用有很多,声明静态全局变量,类的静态成员等。这里主要讨论他在修饰全局变量时与extern的区别。有两点需要注意:
1、static修饰全局变量时,声明和定义是同时给出的;2、static的全局作用域只是自身编译单元。
一个编译单元指一个.cpp文件和他所包含的头文件。也就是说static在修饰全局变量时,“全局”的范围是小于extern的。因此,在头文件中用static声明(和定义)全局变量,是不会出现上面讨论的重复定义的问题的。
还有一点需要说明的是:static也可以修饰局部变量,用static修饰的变量都是保存在静态存储区的,当然全局变量不管用不用static修饰都是存在静态存储区,那全局变量用static修饰和不用static修饰有什么区别呢?区别就在于“全局”的范围是整个工程,还是自身编译单元。