C++中的自动存储
C++有3种管理数据内存的方式即自动存储(栈存储)、静态存储和动态存储(堆存储)。在不同的方式下,内存的分配形式和存在时间的长短都不同。
下面对自动存储进行说明。
自动存储(栈存储)
对于函数的形参、内部声明的变量及结构变量等,编译器将在函数执行时为形参自动分配存储空间,在执行到变量和结构变量等的声明语句时为其自动分配存储空间,因此称其为自动变量(Automatic Variable),有的教科书也称其为局部变量,在函数执行完毕返回时,这些变量将被撤销,对应的内存空间将被释放。
事实上,自动变量的生存期只局限于它所在的代码块。所谓代码块,是包含在花括号对中的一段代码,函数只是代码块的一种,比较下面两段代码。
代码段1
int add(int m,int n)
{
//z的生存期包括整个函数
int z=m+n;
return z;
}
代码段2
int add(int m,int n)
{
if(m!=0)
{
//z的生存期包括在这个代码块中
int z=m+n;
}
return z;//错误
}
在代码段1中,当函数返回时,变量z被撤销,对应内存空间被释放,但在代码段2中,在if代码块中声明的变量z,其生存期仅限于if结构的两个花括号之间,当程序执行到if结构的后花括号时,变量z已被撤销,其对应的内存空间被释放,此时,再执行“return z;”语句便会出错。
注意
理解代码块的含义十分重要,花括号不是判断代码块的唯一标准,把代码段2中if结构的花括号去掉,代码仍然是错误的,将“return z;”放在if结构中是正确的用法。
自动变量的生存期是局部的,这一特性使得程序员可以在不同的块内使用相同的变量名,用不着为使用不同的变量名绞尽脑汁。
1.什么是“栈”
栈(Stack)是一块存储区,而且是C++程序使用最频繁的存储区,其存储机理为“FILO”,即先进后出(First In,Last Out)。可以将其想象成一个装盘子的桶,最早放入的盘子在桶的底部,最晚放入的盘子在最顶部,取盘子时必须先从后放进去的盘子开始取,这就是所谓的先进后出原则。当一个代码块(包括函数,视为一种特殊的代码块)声明一个自动变量时,系统便为其在栈中开辟内存空间(常称“压入”push),该代码块结束后便将自动变量撤销,释放内存空间(常称“弹出”pop)。
注意
采用“栈”这种机制,C++程序能有效地节省所用内存空间。
2.auto关键字
auto是C++提供的存储类声明符,用于声明自动变量,除了auto声明符外,C++还提供了另外3个存储类声明符,分别是register(寄存器存储)、extern(外部存储)和static(静态存储)。在声明创建变量时,存储类声明符应放在数据类型声明符之前,如下所示。
存储类声明符 数据类型 变量名[=初始化表达式]
其中,初始化表达式是可选的,如下列代码声明创建了int型自动变量A,其只在函数demo()执行期间存在,demo()函数执行完毕后,变量A被撤销,对应内存被释放。
void demo()
{
……
auto int A;
……
}
在前面给出的示例代码在声明自动变量时并没有加auto修饰符,实际上,auto常常可以默认,凡是在函数内部(不论是main()函数还是其他函数)的,没有用其他显式的存储类型声明符,编译器都认为是auto型自动变量。
3.register关键字
除了auto外,还可以通过存储类声明符register来声明自动变量,与auto唯一的不同在于:关键字register通知编译器,用户希望通过CPU寄存器,而不是“栈”来处理某个变量,从而可以在一定程度上加快该变量的访问速度。
提示
一般来说,CPU对寄存器的访问要快过对内存的访问。
用register声明的变量常称为寄存器变量,举例来说,下列代码声明了int型寄存器变量sum,并将其初始化为9,如下所示。
register int sum=9;
需要注意的是,即使用register声明了某个变量,编译器也不一定会满足它的要求。因为,CPU寄存器可能被占用或者无法存储指定类型的数据等,而且,现在的编译器一般可以自动决定应把哪些变量放在CPU寄存器中,因此,在C++程序中,register关键字很少使用。
使用register关键字会带来一定的负面效果,不管是否能满足要求,编译器认为register型自动变量是存储在CPU寄存器中的,而寄存器是没有内存地址的,所以,不能对register型自动变量进行取地址操作,下列代码是错误的。
void demo()
{
……
register int sum=0;
int*pSum=∑
……
}
注意
事实上,用auto和register声明的变量除了存储位置不同(一个是“栈”,而另一个可能是“栈”也可能是CPU寄存器)外,并无其他差异,我们可以将其统称为自动变量来考虑。
4.自动变量的初始化
可以在声明自动变量时对其进行初始化,也可以使用任何具有确定值的表达式为自动变量赋值,下列语句都是合法的(假定n为int型自动变量)。
n=2;
n=5*m;//m的值确定
n=add(4,6);
需要特别注意的是,如果没有在自动变量声明的同时对其初始化,其初始值是随机、不可预料的,为避免随机的初始值给程序带来麻烦,推荐在声明自动变量的同时对其显式初始化。