3.11 作用域规则
程序中一个标识符有意义的部分称为其作用域。例如,块中声明局部变量时,其只能在这个块或这个块嵌套的块中引用。一个标识符的4个作用域是函数范围(function scope)、文件范围(filescope)、块范围(block scope)和函数原型范围(function-prototype scope)。后面还要介绍第五个——类范围(class scope)。
任何函数之外声明的标识符取文件范围。这种标识符可以从声明处起到文件末尾的任何函数中访问。全局变量、任何函数之外声明的函数定义和函数原型都取文件范围。
标号(后面带冒号的标识符,如start:)是惟一具有函数范围的标识符。标号可以在所在函数中任何地方使用,但不能在函数体之外引用。标号用于switch结构中(如case标号)和goto语句中(见第18章)。标号是函数内部的实现细节,这种信息隐藏(infomation hiding)是良好软件工程的基本原则之一。
块中声明的标识符的作用域为块范围。块范围从标识符声明开始,到右花括号(})处结束。函数开头声明的局部变量的作用域为块范围,函数参数也是,它们也是函数的局部变量。任何块都可以包含变量声明。块嵌套时,如果外层块中的标识符与内层块中的标识符同名,则外层块中的标识符“隐藏”,直到内层块终止。在内层块中执行时,内层块中的标识符值是本块中定义的,而不是同名的外层标识符值。声明为static的局部变量尽管在函数执行时就已经存在.但该变量的作用域仍为块范围。存储时间不影响标识符的作用域。
只有函数原型参数表中使用的标识符才具有函数原型范围。前面曾介绍过,函数原型不要求参数表中使用的标识符名称,只要求类型。如果函数原型参数表中使用名称,则编译器忽略这些名称。
函数原型中使用的标识符可以在程序中的其他地方复用,不会产生歧义。
常见编程错误 3.20
如果在内层块和外层块中使用同名标识符,而程序员又希望在内层块中引用外层块中的标识符,这通常会产生逻辑错误,因为实际上使用的还是内层块中标识符的值。
编程技巧 3.10
避免隐藏外层块范围名称的变量名,因此要在程序中避免重复使用标识符。
下面的程序演示了全局变量、自动局部变量和 static 局部变量的作用域问题。
// Fig. 3.12:fig03 12.cpp
// A scoping example
#include <iostream.h>
void a(void); // function prototype
void b(void); // function prototype
void c void ); // function prototype
int x = 1; // global variable
int main()
{
int x - 5; // local variable to main
cout << "local x in outer scope of main is " << x << endl;
{ // start new scope
int x = 7;
cout << "local x in inner scope of main is " << x << endl;
} // end new scope
cout << "local x in outer scope of main is" << x << endl;
a(); // a has automatic local x
b(); // b has static local x
c(); // c uses global x
a(); // a reinitializes automatic local x
b(); // static local x retains its previous value
c(); // global x also retains its value
cout << "local x in main is " << x << endl;
return 0;
}
void a(void)
{
int x = 25; // initiallzed each time a is called
cout << endl
<< "local x in a is " << x
<< " after entering a" << endl;
++x;
cout << "local x in a is " << x
<< "before exiting a" << endl;
}
void b(void) static int x = 50; // Static initialization only
// first time b is called.
cout << endl
<< "local static x is " << x
<< -on entering b " << endl;
++x;
cout << "local static x is" << x
<< "on exiting b" << endl;
}
void c(void)
cout
<< endl
<< "global x is " << x
<< "on entering c" << endl;
x *= 10;
cout << "global x is " << x << "on exiting c" << endl;
}
输出结果:
local x in outer scope Of main is 5 local x in inner scope Of main iS 7 local x in outer scope Of main is 5 local x in a is 25 after entering a local x in a ls 26 before exiting a local static x is 50 On entering b local static x is 51 On exiting b global x is 1 on entering c global x is 10 on exiting c local x in a is 25 after entering a local x in a is 26 before exiting a local static x is 51 on entering b local statzc x is 52 On exiting b global x is lO On entering c global x is 100 On exiting c local x in main is 5
全局变量x声明并初始化为1。这个全局变量在任何声明x变量的块和函数中隐藏。在main函数中,局部变量x声明并初始化为5。打印这个变量,结果表示全局变量x在main函数中隐藏。然后在main函数中定义一个新块,将另一个局部变量x声明并初始化为7,打印这个变量,结果表示其隐藏main函数外层块中的x。数值为7的变量x在退出这个块时自动删除,并打印main函数外层块中的局部变量x,表示其不再隐藏。程序定义三个函数,都设有参数和返回值。
函数a定义自动变量x并将其初始化为25。调用a时,打印该变量,递增其值,并在退出函数之前再次打印该值。每次调用该函数时,自动变量x重新初值化为25。函数b声明static变量x并将其初始化为10。声明为static的局部变量在离开作用域时仍然保持其数值。调用b时,打印x,递增其值,并在退出函数之前再次打印该值。
下次调用这个函数时,static局部变量x包含数值51。函数c不声明任何变量,因此,函数c引用变量x时,使用全局变量x。调用函数c时,打印全局变量,将其乘以10,并在退出函数之前再次打印该值。下次调用函数c时,全局变量已变为10。最后,程序再次打印main函数中的局部变量x,结果表示所有函数调用都没有修改x的值,因为函数引用的都是其他范围中的变量。