1.3 生存期和作用域
1.3.1 生存期
不少人对于生存期有着一种错误的理解,认为变量离开了它的作用域,其生存期就结束了。产生这种误解的原因,是对于生存期的概念理解不深刻。所谓的生存期,其实是指变量占用内存或者寄存器的时长。根据变量存储类别的不同,在编译的时候,变量将被存放到动态存储区或静态存储区中,所以其生存期是由声明时的存储类别所决定的。
在讲解存储类别和相应的变量之前,我们先来看看静态存储区和动态存储区。
静态存储区,存放全局变量和静态变量,在执行程序前分配存储空间,占据固定的存储单元。
动态存储区,存放的是函数里的局部变量、函数的返回值、形参等,它在函数被执行的过程中进行动态分配,在执行完该函数时自动释放。由于这种分配和释放都是每次执行到函数时进行的,因此前后两次调用同一个函数,其临时变量分配到的地址可能是不同的。
了解了动态存储区和静态存储区之后,接下来介绍存储类别和相应的变量。
(1)自动(auto)
非静态变量的局部变量即为自动变量,其类型说明符为auto,在C语言中,将函数内没有存储类别说明的变量均视为自动变量,即自动变量可以省去说明符auto。如:
void print()
{
int a;
}
等价于
void print()
{
auto int a;
}
(2)寄存器(register)
指定了register存储类别的变量即为寄存器变量。使用寄存器变量是为了提高执行效率,因为频繁地从内存单元存取变量相比于从寄存器中存取变量需要消耗更多的时间,所以使用register声明的寄存器类型的变量存放在寄存器中,不会占用内存单元,可以提高程序的执行效率。值得注意的一点是,只有局部变量才可以定义成寄存器变量。为了加深读者的印象,我们通过下面两段代码来对比不使用register和使用register的程序执行效率。
注意 以下两段代码均在Linux环境下采用gcc编译运行。
不用register的程序如下:
include<stdio.h>
include<sys/time.h>
int main(int argc,char*argv[])
{
struct timeval start,end;
gettimeofday(&start,NULL);/测试起始时间/
double timeuse;
double sum;
int j,k;
for(j=0;j<1000000000;j++)
for(k=0;k<10;k++)
sum=sum+1.0;
gettimeofday(&end,NULL);/测试终止时间/
timeuse=1000000*(end.tv_sec-start.tv_sec)+end.tv_usec-start.tv_usec;
timeuse/=1000000;
printf("运行时间为:%f\n",timeuse);
return 0;
}
不用register的程序的运行结果:
root@ubuntu:/home#./ce
运行时间为:35.608037
用register的程序如下:
include<stdio.h>
include<sys/time.h>
int main(int argc,char*argv[])
{
struct timeval start,end;
gettimeofday(&start,NULL);/测试起始时间/
double timeuse;
register double sum;
register int j,k;
for(j=0;j<1000000000;j++)
for(k=0;k<10;k++)
sum=sum+1.0;
gettimeofday(&end,NULL);/测试终止时间/
timeuse=1000000*(end.tv_sec-start.tv_sec)+end.tv_usec-start.tv_usec;
timeuse/=1000000;
printf("运行时间为:%f\n",timeuse);
return 0;
}
用register的程序的运行结果:
root@ubuntu:/home#./ce
运行时间为:9.678347
对比上面的两个运行结果,我们发现,使用了register的程序执行速度提高了近3倍,但是读者要注意,虽然可以使用register来提高程序的执行速度,但是也不能大量使用register,因为寄存器的数目是有限的。
(3)静态(static)
关于静态变量,值得注意的一点是,它的生存期是从程序开始运行到程序运行结束。静态变量不属于动态存储,是静态存储。
静态局部变量的生存期虽然是从程序开始运行到程序运行结束,但是它的作用域并不会因此而改变,而且仍然与其作为自动变量的作用域相同。静态全局变量的特点是,它只能在被定义的源程序文件中使用,即它只能被本源程序文件的函数调用,而不能被其他的源程序文件中的函数调用。
静态局部变量和静态全局变量的定义形式都是在数据类型前加上一个静态存储定义符static。但是值得注意的是,两者的初始化方式不同,静态局部变量在它所在的函数被执行时初始化,之后再次执行该函数时,该静态局部变量不再进行初始化,其中保留的是上一次的运行结果;而静态全局变量的初始化是在执行main()函数之前完成的,其静态全局变量的当前值由最近一次对它的赋值操作决定。
在此,我们重点来看看静态局部变量的使用。
include<stdio.h>
void print(void)
{
static int a=0;
printf("静态局部变量a=%d\n",a++);
}
int main(void)
{
print();
print();
return 0;
}
运行结果:
静态局部变量a=0
静态局部变量a=1
分析运行结果可以得知,静态局部变量在初始化以后,再次执行该函数时静态局部变量保存的是上一次的运行结果。
(4)外部(extern)
外部存储类别定义方式为在全局变量类型前面加上关键字extern,如果没有指定全局变量的存储类别,则默认为extern。