对一个C++变量来说,有两个属性非常重要:作用域和生命周期,它们从两个不同的维度描述了一个变量--时间和空间。顾名思义,作用域就是一个变量可以被引用的范围,如:全局作用域、文件作用域、局部作用域;而生命周期就是这个变量可以被引用的时间段。不同生命周期的变量,在程序内存中的分布位置是不一样的。一个程序的内存分为代码区、全局数据区、堆区、栈区,不同的内存区域,对应不同的生命周期。
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
请看下面例子:
- #include<iostream>
- using namespace std;
- char* func()
- {
- //char str[]="Hello,world\n"; //error 字符串数组,局部变量,存储在栈区;可以修改
- char *str="Hello,world\n"; //okay 字符串常量,存储在常量区;不可以修改
- return str;
- }
- int main()
- {
- cout << func();
- return 0;
- }
有很多方法来指定一个变量的作用域和生命周期。最常见的,如:{ }、static修饰符等。下面按照作用域与生命周期来对变量做一个分类:
全局变量
- 作用域:全局作用域(全局变量只需在一个源文件中定义,就可以作用于所有的源文件。)
- 生命周期:程序运行期一直存在
- 引用方法:其他文件中要使用必须用extern 关键字声明要引用的全局变量。
- 内存分布:全局数据区
- 注意:如果在两个文件中都定义了相同名字的全局变量,连接出错:变量重定义
- 例子:
- //defime.cpp
- int g_iValue = 1;
- //main.cpp
- extern int g_iValue;
- int main()
- {
- cout << g_iValue;
- return 0;
- }
全局静态变量
-
作用域:文件作用域(只在被定义的文件中可见。)
-
生命周期:程序运行期一直存在
-
内存分布:全局数据区
-
定义方法:static关键字,const 关键字
-
注意:只要文件不互相包含,在两个不同的文件中是可以定义完全相同的两个静态变量的,它们是两个完全不同的变量
-
例子:
- const int iValue_1;
- static const int iValue_2;
- static int iValue_3;
- int main()
- {
- return 0;
- }
静态局部变量
-
作用域:局部作用域(只在局部作用域中可见)
-
生命周期:程序运行期一直存在
-
内存分布:全局数据区
-
定义方法:局部作用域用中用static定义
-
注意:只被初始化一次,多线程中需加锁保护
-
例子:
- void function()
- {
- static int iREFCounter = 0;
- }
局部变量
-
作用域:局部作用域(只在局部作用域中可见)
-
生命周期:程序运行出局部作用域即被销毁
-
内存分布:栈区
-
注意:auto指示符标示
有如下笔试题:
全局变量放在(数据段);函数内部变量Static int ncount 放在(数据段);函数内部变量char *p="AAA",p的位置在(堆栈);指向空间的位置(数据段);函数内变量char *p=new char;p的位置(堆);指向空间的位置(数据段);
还有一点要说明,掌握static关键字的使用很关键。以下是引用别人的一些经验之谈:
Tips:
-
若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
-
若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
-
设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见;
-
如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
-
函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
-
用new关键字或者malloc函数分配的内存空间既不在栈中,也不在静态数据区,在堆中。
堆和栈的区别:
1、管理方式不同;对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
2、空间大小不同;栈的空间有限,堆有很大的自由存储区
3、能否产生碎片不同;堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
4、分配效率不同;在栈上的数组比指针所指向的字符串(例如堆)快。