首页 理论教育C语言程序设计基础(第3版):变量生存期

C语言程序设计基础(第3版):变量生存期

【摘要】:图5.12extern关键字对全局变量的作用例5.16程序中在第12行定义了全局变量x并赋初值为100,根据定义其作用域为第12~17行所构成的区间。图5.13extern关键字对全局变量的作用在图5.13中标注为②的源程序文件中定义了全局变量y,该全局变量默认的作用域范围为其定义所在的整个源程序文件。自动变量的生存期与其所在函数被调用运行的时间相同,并且自动变量的值在函数的多次调用中都不会保留。

程序运行过程中,变量存在的时间(生存期)与其在系统存储器中占据的存储位置相关。在C语言中使用关键字auto、register、static和extern规定程序中变量的存储类别,不同存储类别的变量,不但占用的存储区域不同,而且C系统为它分配存储的时间也不同。

C程序运行过程中,计算机系统将其使用到的存储器分为两个区域:静态存储区域和动态存储区域。在C程序执行的整个过程中,全局变量和稍后要讨论到的静态变量(全局或局部)是存放在系统存储器静态存储区域中的,而且系统在对C程序进行编译时就已经为这类变量分配好了存储空间,因而这类变量的存在时间(生存期)是C程序的整个运行期间;而自动变量在程序执行过程中被使用到时系统才会为它们分配存储空间,而且自动变量是存放在系统存储器动态存储区域中的,所以自动变量的存在时间(生存期)只是在程序运行过程中使用到该变量的时间段内。

为了达到在C程序设计中合理选择使用变量的目的,从使用的目的出发,对C语言中提供的存储类别关键字以及它们在程序设计中对变量的作用上分为以下三个方面讨论:

·寄存器型存储类别关键字register的作用。

·外部存储类型关键字extern、静态存储类别关键字static与全局变量之间的关系。

·自动存储类别关键字auto、静态存储类别关键字static与局部变量的关系。

1.register关键字

用关键字register限定的变量称为寄存器变量,所谓寄存器变量指的是将其值存放在CPU寄存器中的变量。由于对寄存器的使用不经过系统内存,故寄存器变量是在程序执行过程中存取速度最快的变量类型。在C程序设计中,可以考虑将使用频率较高的变量定义为register型变量,以提高程序执行的速度。但在C程序设计中使用register存储类型的变量必须理解以下两点:

①在C程序中并不是所有的变量都可以使用寄存器存储类别关键字register来限定,register存储类型只能作用于整型(或字符型)的非函数形式参数局部变量,不能作用于全局变量,也不能作用于实型变量或者其他构造类型变量。

②寄存器类型的变量是否起作用取决于C程序运行所处的软硬件环境。在C程序设计中,使用关键字register限定变量只是表达了使用者的一种愿望,该愿望是否能实现取决于使用的系统硬件环境和编译系统。在能够识别并处理register存储类型变量的编译系统环境中,如果CPU中没有足够的寄存器供寄存器变量使用,则编译系统自动将超过限制数量的寄存器变量作为自动变量(auto)处理;还有一些编译系统虽然能够识别register存储类型变量,但对register存储类别并不进行处理,而是将寄存器类型变量一律作为自动变量处理。

2.extern和static关键字与全局变量的关系

对于C程序中的全局变量,编译系统将其存储区域分配在系统的静态存储区,而且编译系统在对C程序进行编译的时候就对全局变量进行存储分配和初始化处理,因而全局变量的存在时间(生存期)是整个程序的运行周期。对于全局变量而言,能够起作用的关键字为extern和static两个。

使用关键字extern对全局变量重新声明可以将全局变量的作用域在本源程序文件内扩充或者扩充到同一C程序的其他源程序文件中。

从图5.12中可以看出,在C源程序文件中较后部分定义了一个全局变量x,根据其定义位置得到了原作用域,可以看出该全局变量x在其所处的源程序文件上半部分不能起作用。在源程序文件的较前位置对全局变量x用C语句extern int x;进行了重新声明,通过这种对全局变量的重新声明,使得全局变量x的作用域从原来所定义的区域扩充(大)到了对其重新声明处。

【例5.16】 全局变量作用域在一个源程序文件C程序中的扩充示例(为了讨论方便加上行号)。

图5.12 extern关键字对全局变量的作用

例5.16程序中在第12行定义了全局变量x并赋初值为100,根据定义其作用域为第12~17行所构成的区间。在程序中的第7行、8行都要使用到全局变量x,但在默认的情况下,全局变量x在这些范围内无定义,即默认情况下这些对全局变量x的操作是非法的。程序在第3行对全局变量x用C语句extern int x;进行了重新声明,使得全局变量的作用范围扩充到了从第3行开始至第17行为止,从而使得在第7、8行中对全局变量x的操作可以顺利进行。如果不在第3行对全局变量x的作用域进行扩充,则该程序在编译时会出现编译错误:error C2065:'x':undeclared identifier,读者可将上面程序的第3行去掉(注释掉)后了解这种情况。例3.16程序执行的结果为:

主函数中的输出:110

函数f中的输出:130

同样使用关键字extern对全局变量重新声明可以将全局变量的作用域扩充到其定义所在的源程序文件之外。

图5.13 extern关键字对全局变量的作用

在图5.13中标注为②的源程序文件中定义了全局变量y,该全局变量默认的作用域范围为其定义所在的整个源程序文件。全局变量y在没有进行重新声明的情况下在标注为①的源程序文件中是没有定义的。为了将全局变量y的作用域扩充到标注为①的源程序文件中,在标注为①的源程序文件中使用C语句extern int y;对全局变量y进行了重新声明。(www.chuimin.cn)

如果不允许将全局变量的作用范围扩充到同一程序的其他源程序文件,可以使用关键字static在定义全局变量时予以限定,即定义静态的全局变量。

全局变量作用域在两个以上源程序文件构成的C程序中进行扩充的问题涉及如何处理有多个C语言源程序文件的问题,请读者参考其他教学资料。

3.auto和static关键字与局部变量的关系

对局部变量能够起作用的关键字为auto和static两个。如前所述,用auto说明的局部变量称为自动变量,系统在调用函数时才对函数中所定义的自动变量在动态存储区域中分配存储并按要求进行初始化,一个自动变量如果没有被初始化则其初始值是随机的。自动变量的生存期与其所在函数被调用运行的时间相同,并且自动变量的值在函数的多次调用中都不会保留。

如果希望(要求)某些局部变量不随着函数调用过程的结束或复合语句执行结束而消失,即期望当程序执行的控制流程再次进入这些局部变量所存在的函数或复合语句时,这些变量仍能在保持原值基础上继续被使用,C程序中可以使用静态局部变量满足这种需求。静态局部变量定义的一般形式是:

static<数据类型符>变量表;

C程序中的静态局部变量具有如下特点:

·静态局部变量的存储位置。编译系统在编译时就为静态局部变量在系统静态存储区域中分配存储空间,静态局部变量的存储空间在程序的整个运行期间是固定的。因而静态局部变量的生存期是整个程序的运行周期。

·静态局部变量的初始化。静态局部变量的初始化是在源程序被编译时进行的。如果在定义静态局部变量时没有对它进行显式的初始化,编译系统会自动将其初始化为0(若是字符类数据则初始化为'\0')。

·静态局部变量的作用域(作用范围)。静态局部变量也是局部变量,它的值也只能在定义它的局部范围内使用,即静态局部变量作用域界定方法与自动局部变量作用域的界定方法是相同的。离开静态局部变量的作用域后,该静态局部变量虽然存在,但不能对它进行访问(操作)。

·静态局部变量具有继承性。在某个函数中定义的静态局部变量值在函数的多次调用中具有可继承性,即对于某函数中的静态局部变量而言,在函数被多次调用时是同一变量。

【例5.17】 静态局部变量与自动变量的比较示例(为了讨论方便加上行号)。

上面程序的f1函数中,在第12行定义了自动变量a,初始值为10;在第13行定义了静态局部变量b,初始值为10。程序在执行时,第6行第一次调用函数f1,此时系统会为自动变量a分配存储(即创建该变量)并初始化为10;对于静态局部变量b而言,在程序编译时就分配了存储,即此时该变量已经是存在的;第14行和第15行分别对变量a和变量b增加值100,使得变量a和b的值都为110,程序输出:a=110,b=110;输出结果后函数f1执行完成,程序的控制流程返回到主函数中的第6行。此时,在函数f1中定义的变量a被系统自动撤销;根据静态局部变量的特点,变量b仍然存在,但由于此时控制流程在主函数中,已经离开了静态局部变量b的作用域,所以静态局部变量b虽然存在但在主函数中不能使用。程序在第7行第二次调用了函数f1,对于自动变量a系统重新为其分配存储(即重新创建该变量)并初始化为10,但对于静态局部变量b则不会重新创建,使用的就是上一次调用时使用的变量b(此时变量b的值为上一次操作后的结果110);所以在第二次f1函数的调用中程序输出的结果为:a=110,b=210。

上面我们从使用的角度出发,分析了C语言中4个存储类别符的使用方法。表5.1对变量存储位置与变量作用域以及生存期的关系进行了小结。

表5.1 变量存储位置与作用于和生存期的关系