全局变量,就是指那些定义在函数或类之外的变量,它可作为多个类或函数共同使用的变量。
(1)全局变量会被自动初始化,函数中的变量不会被自动初始化,类中定义的那些成员变量(内建)也不会自动初始化。那么这里有一个疑问,为什么要这样设置?并且为什么,进程内存区中,分为初始化了的全局变量和静态变量,和未初始化的全局和静态变量?
(2)全局变量如果加上static关键字,事情将会变得很奇妙。
我们知道,全局变量理论上是指应用程序级别的全局。普通的全局变量是面向整个程序的,当各个文件各自编译,然后链接成一个可执行程序之后,全局变量的确是能被所有文件“看到的”,但是,如果想让别的文件在编译程序的时候就能够看到,(如果你不做然后措施,直接使用另外一个文件中定义的变量,那将通不过编译)
有两个办法:
第一个就是#include方法,但是这个方法,其实是将文件合并成一个文件,并没有正面回答。
第二个就是在要使用该变量的文件中声明(extern)一个外部变量,这样等于就是告诉编译器,“该变量是有的,只是在另外一个文件中,等到链接的时候你就可以看到了。”这个extern必须要加,否则就是重新定义一个变量,到时候连接会出现重定义问题。
但是,如果你在全局变量前加上static,那么它的作用范围就变小了,变成了文件范围。这就会导致该变量是不容许别的文件通过extern声明方式来操作。因为该变量对其它文件是不可见的。
我们提到变量的生命周期和作用域时有这样的说法:
1) 全局变量和静态变量的生命周期为整个进程,他们都处在内存的同一个区域
2) 普通全局变量的作用域为全局(整个软件,可跨域文件,其他文件想使用可以使用extern声明),函数中的静态变量的作用域为该函数。
3) 静态全局变量的作用域则被限制在该文件中,所以,这个时候其他文件通过extern想使用该变量是不行的,(就算成功啦,那是因为你引用了其他文件中的定义)。由于作用域被限制在当前文件,不同的文件定义自己的全局变量就不会担心与其他文件出现冲突。
一直以来,有一个有关全局变量定义的标准:
1) 如果在这个文件中定义的全局变量不打算给别人用,那么你就将它定义为static全局变量吧!因为这样你不必担心其他文件也定义了一个同名变量,在连接的时候出现重定义。
2) 如果你的全局变量是打算给其他文件使用的,那么就不要加上static,因为这样在其他文件中可以使用extern对该定义进行引用。
这么说来,static 和extern是不能用来修饰同一个变量的,extern修饰表示该变量只是声明,声明它使用了其他文件的变量定义,static的修饰表示我这个变量(自己定义的),只能被当前文件访问。两者完全冲突,所以编译器会报错——‘n’的声明中有相互冲突的限定符。
函数或变量在声明时,并没有给它实际的物理内存空间,它有时候可以保证你的程序编译通过, 但是当函数或变量定义的时候,它就在内存中有了实际的物理空间,如果你在编译模块中引用的外部变量没有在整个工程中任何一个地方定义的话, 那么即使它在编译时可以通过,在连接时也会报错,因为程序在内存中找不到这个变量!你也可以这样理解, 对同一个变量或函数的声明可以有多次,而定义只能有一次!
在IDE开发工具大行其道的今天,对于编译的一些概念很多人已经不再清楚了,很多程序员最怕的就是处理连接错误(LINK ERROR), 因为它不像编译错误那样可以给出你程序错误的具体位置,你常常对这种错误感到懊恼,但是如果你经常使用gcc,makefile等工具在linux或者嵌入式下做开发工作的话,那么你可能非常的理解编译与连接的区别!当在VC这样的开发工具上编写完代码,点击编译按钮准备生成exe文件时,VC其实做了两步工作,第一步,将每个.cpp(.c)和相应.h文件编译成obj文件;第二步,将工程中所有的obj文件进行LINK生成最终的.exe文件,那么错误就有可能在两个地方产生,一个是编译时的错误,这个主要是语法错误,另一个是连接错误,主要是重复定义变量等。我们所说的编译单元就是指在编译阶段生成的每个obj文件,一个obj文件就是一个编译单元,也就是说一个cpp(.c)和它相应的.h文件共同组成了一个编译单元,一个工程由很多个编译单元组成,每个obj文件里包含了变量存储的相对地址等 。
Extern有两种用法:
(1) 与C一起使用 extern C ,来在C++中表达,在编译的时候按照C的风格进行编译。
(2) 修饰普通全局变量的声明,(extern int n;)注意它只能是声明,不能定义初始化,它声明表示,该变量可以从另外一个文件中找到,现在大胆使用就行了。到时候(链接的时候)就可以找到了,所以我认为这个extern关键是来支持,当前文件可以通过编译器的检查。当前文件使用了另外一个文件内定义的东西,但是又不能直接#include它,所以这导致这种技术的出现。注意,必须有一个文件定义了该变量(int n;),如果没有一个文件定义了它,那么编译的时候能够通过,但是在连接的时候,文件找不到你给它的承诺,就会报错——undefined reference to ‘n'。
当extern不与"C"在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可, 在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
Static的应用场景:
(1) 在C++面向对象中修饰成员函数或变量,这表明它所属范围为这个类,而不是对象。
其实,这个时候,我们完全可以将它理解为,使用范围限定在类中的全局变量.(其实完全就是这样,静态变量和全局变量,其实是联系很紧密的,在内存中,他们存储的位置是一样的,生命周期也是一样的,关键在于他们的可视范围发生的变化而已。
(2) 在局部变量中(函数中)修饰变量,表示该变量是这个函数范围内的不变的,而不是随着每一次的函数被调用而更新。所以你会发现,static修饰函数中的变量和修饰类中的成员有异曲同工之妙。函数每次被调用,类似于类每次被实例化。普通的成员都会是一个新的,而static成员与至始至终属于该函数(类)。
(3) 在全局变量中修饰变量,表示该变量的作用范围为当前文件,而不是整个程序。
static全局变量与普通的全局变量有什么区别 ? static局部变量和普通局部变量有什么区别? static函数与普通函数有什么区别?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
注:原来理解全局变量就是静态全局变量,正好与事实相反。关于变量的声明和定义,个人认为是针对于extern来说的,加了extern的肯定只是声明,但是不加extern的不太好区分声明和定义(如果有人懂的话请给我讲解一下,谢谢)。