我们在学习c/c++的时候,最开始接触的就是变量的声明和定义。对于这两个概念大家都很熟悉,但是具体研究下来,往往比较模糊,尤其是在代码跟踪调试,对于ide提供的跳转到声明和跳转到定义,不能明确区分其差别。包括后续开发,全局变量的使用的时候,往往有各种编译问题,还有extern static等等对变量的影响。今天就根据个人经验详细说一下变量的声明和定义,以及在使用过程中容易出现的一些错误。
字面意思
按照最常见的解释,声明是仅仅告诉程序有这么一个数据结构类型或者函数类型;定义就是对数据结构或者函数进行设计,实现,能让程序找到最终的数据所在。
这与分配内存很像,c中申请一块内存,并没有在真实物理内存上分配空间,只有当第一次使用的时候,才会触发一个中断,然后到物理内存上分配空间。所以申请内存就相当于声明,分配物理内存就相当于定义。
在很多时候声明和定义是一条语句,所以非常容易混淆。
具体例子
//声明
extern int a;
//如果第一次出现,或者没有extern int a;,这个就是声明和定义,如果前面有extern int a;表示已经声明了,这里就是定义
int a = 1;
//声明
struct s;
//定义
struct s{
int num;
}
//如果是第一次出现或者没有出现过struct s;,这里就是声明定义,如果出现过struct s;,这里就是定义
struct s{
int num;
}
声明可以有多次声明,但定义只能有一次定义
声明可以在多个地方对同一个内容声明,但是记住,在同一作用域内,只能声明一次
比如在同一个函数中,不可以对同一变量声明两次
//这样是会报错的
extern int a;
extern int a;
但是对于同一变量在不同作用域下,可以重复声明,不同文件也属于不同作用域
//下面这样写是没问题的
//a.h
extern int a;
//a.c
extern int a;
//b.h
extern int a;
对于同一变量,不管在哪个作用域,都只能定义一次
不管在哪个作用域,只要是指向同一个变量,多次定义都会报错,因为程序不知道以哪一个为准,大体报错内容如下
multiple definition of first defined here
error: ld returned 1 exit status
//如下就会报错
//a.h
extern int a;
//a.c
int a = 1;
//b.h
extern int a;
//b.c
int a = 2;
为什么没有用int a;举例
上面的例子可以看到,只写了extern int a;和int a = 1;,没有用int a;举例,是因为int a的含义模糊,可能是extern int a省略了extern,表示声明;也可能是int a;定义,根据不同情况会不一样。
之声明不定义可以吗
不可以,如果你在很多地方写了extern int a,但是没有任何一个地方写int a;或者int a = 1;,那么你直接用a的时候,不管是赋值还是输出,都会报错,告诉你没有定义,报错日志如下
undefined reference to
error: ld returned 1 exit status
//如下会报错
//a.h
extern int a;
//a.c
a = 1;
extern常见的使用
extern "C"
因为c++为了实现类的多态等功能,每个函数编译完成后会在函数名字后面加一串字符,也就是函数名字已经被修改了,这样的话是无法写lib库的,因为lib库会根据你提供的函数名到编译好的库中查找函数位置,而c++编译出来的可执行文件中的函数名是未知的。为了避免这个问题,可以用这个把需要提供给别人,不想修改函数名的函数括起来
全局变量
c中的全局变量,因为少了c++类的限制,很容易在使用中出现问题。比如a.h中定义了全局变量,b.h/b.c和c.h/c.c都会使用,这时就比较麻烦,很容易出现重复定义的问题。
比如全局变量是int a;
都不引用a.h
如果不引用a.h,那么在b.h/b.c中使用a时就会报错,提示为定义,那么就要在用前声明extern int a;,这样告诉编译器,可以到其他文件中查找是否定义了a。这样做法就是关系不明确,因为没有引入头文件;繁琐,因为每次使用前都要用extern int a;声明
如果a.h中是int a;,并且引用a.h
这样使用时相对问题少一点,但是有一个不方便的就是int a没有初始化,之所以这样没问题就是因为上面说的int a;的意义是不明确的,编译器会根据需要来确定是声明还是定义。
如果a.h中是int a = 1;,并且引用a.h
这是明确定义的,那么多个文件引用a.h就会报重复定义
如果a.h中是extern int a;,并且引用a.h
这样写是目前大家处理全局变量的常用方法,这里仅仅是声明,所以需要有一个定义,那么正好在a.c中进行定义初始化——int a = 0;
这样在其他文件中引用a.h,都不需要额外做什么,直接用a即可,这也符合上面的逻辑,就是所有用到的地方都是extern int a;,真正定义的只有a.c中int a = 0;