6.2 变量的作用域
在上一节中,介绍了函数的基本知识,了解了函数的使用。那么,在调用函数的过程中,在函数中使用的各种变量的作用范围是多大呢?本节就来介绍函数中变量的作用域。
作用域规则告诉一个变量的有效范围,它在哪儿创建,在哪儿销毁(也就是说超出了作用域)。变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号。也就是说,作用域由变量所在的最近一对括号确定。
6.2.1 局部变量
局部变量是指限制在某一范围内使用的变量,局部变量经常被称为自动变量,因为它们在进入作用域时自动生成,采用堆栈方式分配内存空间,离开作用域时,释放内存空间,值也自动消失。关键字auto可以显式地说明这个问题,但是局部变量默认为auto,所以没有必要声明为auto。
下面通过一个实例来说明局部变量的作用域。
【实例6-5】局部变量的作用域(代码6-5.txt)
新建名为“jbbltest”的【C++ Source File】源程序,源代码如下所示:
【代码详解】
在这个例子中,声明了函数t1,接下来在主程序中两次调用了t1;然后,定义了函数t1,在该函数中,首先定义int型变量y,赋值为1,然后y自加1,最后将y的结果输出。
运行结果如图6-6所示。
图6-6 代码运行结果
【实例分析】
从结果来看,两次输出的y的结果是一致的。由于y是局部变量,第一次调用后,值变为2,此次调用后,y被销毁,当再次调用时,y再次被初始化为1,然后变为2。
6.2.2 静态局部变量
静态变量也是一种局部变量,在变量前面加上关键字static,这个变量就被定义为静态变量。
通常,在函数中定义的局部变量在函数作用域结束的时候释放掉内存空间,该变量也就随之消失了。当再次调用该函数的时候,会重新初始化局部变量,之后才可以使用。静态变量与局部变量的不同之处在于,只要程序一直在执行,静态变量定义的值就一直有效,不会随着函数的结束而消失。主要原因是,静态变量在内存中的存放是有固定地址的,而不像局部变量一样使用堆栈方式存取。
下面通过一个实例来说明静态局部变量的作用域。
【实例6-6】静态局部变量(代码6-6.txt)
新建名为“jtjbtest”的【C++ Source File】源程序,源代码如下所示:
【代码详解】
在这个例子中,首先声明了函数t1,在主程序中两次调用了t1;然后,定义了函数t1,在该函数中,定义了int型静态局部变量x并赋值为1,x自加1后将x的结果输出。
运行结果如图6-7所示。
图6-7 代码运行结果
【实例分析】
从结果来看,两次输出的x的结果分别是2和3,第一次调用t1函数后x变为2,第二次调用t1函数时,x在内存中保存的值就是2,x自加1后变为3,即再次调用静态局部变量时,会跳过原来的初始化动作。
6.2.3 外部变量
extern告诉编译器存在着一个变量和函数,即使编译器在当前的文件中没有看到它,这个变量或函数可能在一个文件或者当前文件的后面定义。例如extern int i,编译器会知道i肯定作为全局变量存在于某处。当编译器看到变量i的定义时,并没有看到别的声明,所以知道它在文件的前面已经找到了同样声明的i。
当一个变量成为外部变量之后,不必再次为它分配内存就可以引用这个变量。
下面通过一个实例来说明外部变量的作用域。
【实例6-7】外部变量(代码6-7.txt)
新建名为“wbbltest”的【C++ Source File】源程序,源代码如下所示:
【代码详解】
在该例中,首先声明了max函数,在主程序中,声明了全局变量a和b,接下来调用max函数将a和b中较大的输出;定义全局变量a和b,分别赋值为15和-7;定义max函数,求得最大值。
运行结果如图6-8所示。
图6-8 代码运行结果
【实例分析】
从整个示例来看,输出结果为15。在main函数后面定义了全局变量a和b,但由于全局变量定义的位置在函数main之后,因此如果没有程序的第6行,在main函数中是不能引用全局变量a和b的。现在在main函数第6行用extern对a和b做了提前引用声明,表示a和b是将在后面定义的变量。这样在main函数中就可以合法地使用全局变量a和b了。如果不进行extern声明,编译时就会出错,系统认为a和b未经定义。一般都把全局变量的定义放在引用它的所有函数之前,这样可以避免在函数中多加一个extern声明。
6.2.4 寄存器变量
使用寄存器变量的目的是将变量放入寄存器中,而加快访问速度。使用关键字“register”来声明一个寄存器变量,如果在声明寄存器变量时,系统的寄存器被其他数据占用,寄存器变量就会变为局部变量。
使用register变量是有限制的:
(1)不可能得到或计算register变量的地址。
(2)register变量只能在一个块中声明(不可能有全局的或静态的register变量),然而可以在一个函数中(即在参数表中)使用register变量作为一个形式参数。