图5.7两层函数嵌套调用示意图函数的嵌套调用即一个函数在被调用的过程中又调用了另外的一个函数。函数fac和powers的返回值类型均被设计为double型,其主要目的是为了避免n!x:0.5//0.5从键盘输入的数据0.50 powers of e=1.648721//程序执行结果......
2023-11-20
程序运行过程中,变量存在的时间(生存期)与其在系统存储器中占据的存储位置相关。在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 变量存储位置与作用于和生存期的关系
有关C语言程序设计基础(第3版)的文章
图5.7两层函数嵌套调用示意图函数的嵌套调用即一个函数在被调用的过程中又调用了另外的一个函数。函数fac和powers的返回值类型均被设计为double型,其主要目的是为了避免n!x:0.5//0.5从键盘输入的数据0.50 powers of e=1.648721//程序执行结果......
2023-11-20
例5.12外部变量的使用。我们把例5.12稍加改动:存储类型标识符extern告诉系统,变量类型和名字已在别处定义过了,这里的extern int i;只是说明一下i是外部变量。如果外部变量的定义在使用之前,不用extern说明也可以;但如果定义在使用后面,说明就是不可缺少的了。最后应说明的是,外部变量提供了一种在函数间自由传递数据的机制,为编写程序带来了一些方便。但是,外部变量也有很大的副作用,它破坏了函数的封闭性,使程序的控制复杂起来。......
2023-11-18
针对不同的实际应用,数据排序方法有很多种。本节介绍两种常用排序方法的基本思想和实现方法,帮助读者初步理解排序方法的计算机解决思路。②不考虑已排好序的数据,将剩下的数据作为待排序列。编写程序实现冒泡排序算法,对随机生成的10个3位整数按升序进行排序并输出。......
2023-11-20
图8.1函数内存映射方式1.指向函数指针变量的定义C语言中可以定义指针变量来存储函数的首地址,并利用该指针变量对函数进行调用。指向函数的指针变量的真正作用是作为函数参数使用,使程序在功能的实现上具有更大的灵活性。指向函数的指针变量求最大值、最小值和两数之和。......
2023-11-20
一个函数直接或间接地调用自己,称为函数的递归调用。所以函数递归调用的实现必须依靠系统提供一个特殊部件(堆栈)存放未完成的操作,以保证当递归调用结束回溯时不会丢失任何应该执行而没有执行的操作。为了理解函数递归调用的特性,参照例5.9的程序讨论函数递归调用的执行过程,为了讨论方便为程序加上行号。函数递归调用示例。......
2023-11-20
例如,有如下所示的C语句序列:则结构体指针变量p1指向结构体数组元素a[2],其关系如图10.2所示。此时应该注意到被指针变量p1指向的结构体数组元素本身是不能作为整体操作的,所以*p1也不能作为整体操作。......
2023-11-20
在变量名及其类型之前加上关键字static,就规定该变量的存储类型为静态的。例5.11一个静态变量和自动变量比较的程序。运行结果:auto_var=0,static_var=0auto_var=0,static_var=1auto_var=0,static_var=2auto_var=0,static_var=3auto_var=0,static_var=4上例中,main()函数共5次调用了auto_static()函数,每次调用时自动变量auto_var都输出0值,这是因为每次调用时系统都给auto_var分配存储单元并赋初值0。当auto_static()结束运行时,static_var的值仍然保留,再次调用auto_static()函数时,系统不再为它重新分配存储空间并赋初值,而是采用以前留下的值,因此static_var的值每次调用都会增加1。......
2023-11-18
还可以在枚举类型定义中对枚举元素起始值作多次改变,每次改变后枚举值从该处开始递增直到遇到下一次起始值的指定为止。枚举数据类型定义完成后,仍然需要定义枚举变量才能使用,常见的方法有:①先定义枚举类型,然后定义枚举变量。枚举变量的输入输出示例。......
2023-11-20
相关推荐