6.10. Scope Rules
The portion of the program where an
identifier can be used is known as its scope. For example, when we declare a
local variable in a block, it can be referenced only in that block and in blocks
nested within that block. This section discusses four scopes for an
identifier—function scope, file
scope, block scope and function-prototype scope. Later we'll see two other scopes—class scope (Chapter
9) and namespace scope (Chapter
22).
An identifier declared outside any
function or class has file scope. Such an identifier is "known" in all functions
from the point at which it is declared until the end of the file. Global
variables, function definitions and function prototypes placed outside a
function all have file scope.
Labels (identifiers followed by a colon such as start:) are the only identifiers with function scope.
Labels can be used anywhere in the function in which they appear, but cannot be
referenced outside the function body. Labels are used in goto statements, which we do not cover in this book. Labels
are implementation details that functions hide from one another.
Identifiers declared inside a block
have block scope. Block scope begins at the identifier's declaration and ends at
the terminating right brace (}) of the block in
which the identifier is declared. Local variables have block scope, as do
function parameters, which are also local variables of the function. Any block
can contain variable declarations. When blocks are nested and an identifier in
an outer block has the same name as an identifier in an inner block, the
identifier in the outer block is "hidden" until the inner block terminates.
While executing in the inner block, the inner block sees the value of its own
local identifier and not the value of the identically named
identifier in the enclosing block. Local variables declared static still have block scope, even though they exist from the
time the program begins execution. Storage duration does not affect the scope of
an identifier.
The only identifiers with function
prototype scope are those used in the parameter list of a function prototype. As
mentioned previously, function prototypes do not require names in the parameter
list—only types are required. Names appearing in the parameter list of a
function prototype are ignored by the compiler. Identifiers used in a function
prototype can be reused elsewhere in the program without ambiguity. In a single
prototype, a particular identifier can be used only once.
Common Programming Error 6.12
|
Accidentally
using the same name for an identifier in an inner block that is used for an
identifier in an outer block, when in fact you want the identifier in the outer
block to be active for the duration of the inner block, is normally a logic
error. |
Good Programming Practice 6.4
|
Avoid variable
names that hide names in outer scopes. This can be accomplished by avoiding the
use of duplicate identifiers in a
program. |
The program of Fig. 6.11 demonstrates scoping issues
with global variables, automatic local variables and static local
variables.
Fig. 6.11. Scoping example.
1 // Fig. 6.11: fig06_11.cpp
2 // A scoping example.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 void useLocal(); // function prototype
8 void useStaticLocal(); // function prototype
9 void useGlobal(); // function prototype
10
11 int x = 1; // global variable
12
13 int main()
14 {
15 cout << "global x in main is " << x << endl;
16
17 int x = 5; // local variable to main
18
19 cout << "local x in main's outer scope is " << x << endl;
20
21 { // start new scope
22 int x = 7; // hides both x in outer scope and global x
23
24 cout << "local x in main's inner scope is " << x << endl;
25 } // end new scope
26
27 cout << "local x in main's outer scope is " << x << endl;
28
29 useLocal(); // useLocal has local x
30 useStaticLocal(); // useStaticLocal has static local x
31 useGlobal(); // useGlobal uses global x
32 useLocal(); // useLocal reinitializes its local x
33 useStaticLocal(); // static local x retains its prior value
34 useGlobal(); // global x also retains its prior value
35
36 cout << "\nlocal x in main is " << x << endl;
37 return 0; // indicates successful termination
38 } // end main
39
40 // useLocal reinitializes local variable x during each call
41 void useLocal()
42 {
43 int x = 25; // initialized each time useLocal is called
44
45 cout << "\nlocal x is " << x << " on entering useLocal" << endl;
46 x++;
47 cout << "local x is " << x << " on exiting useLocal" << endl;
48 } // end function useLocal
49
50 // useStaticLocal initializes static local variable x only the
51 // first time the function is called; value of x is saved
52 // between calls to this function
53 void useStaticLocal()
54 {
55 static int x = 50; // initialized first time useStaticLocal is called
56
57 cout << "\nlocal static x is " << x << " on entering useStaticLocal"
58 << endl;
59 x++;
60 cout << "local static x is " << x << " on exiting useStaticLocal"
61 << endl;
62 } // end function useStaticLocal
63
64 // useGlobal modifies global variable x during each call
65 void useGlobal()
66 {
67 cout << "\nglobal x is " << x << " on entering useGlobal" << endl;
68 x *= 10;
69 cout << "global x is " << x << " on exiting useGlobal" << endl;
70 } // end function useGlobal
|
global x in main is 1
local x in main's outer scope is 5
local x in main's inner scope is 7
local x in main's outer scope is 5
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
local x is 25 on entering useLocal
local x is 26 on exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
|
Line 11 declares and initializes global variable x to 1. This global variable is hidden in any block (or
function) that declares a variable named x. In main, line 15
displays the value of global variable x. Line 17 declares a local
variable x and initializes it to 5. Line 19
outputs this variable to show that the global x is hidden in
main. Next, lines 21–25 define a new block in
main in which another local variable x
is initialized to 7 (line 22). Line 24 outputs this variable to show that it
hides x in the outer block of main. When the block exits, the variable x with value 7 is destroyed automatically.
Next, line 27 outputs the local variable x in
the outer block of main to show that it is no
longer hidden.
To demonstrate other scopes, the
program defines three functions, each of which takes no arguments and returns
nothing. Function useLocal (lines 41–48) declares automatic variable
x (line 43) and initializes it to 25. When the
program calls useLocal, the function prints
the variable, increments it and prints it again before the function returns
program control to its caller. Each time the program calls this function, the
function recreates automatic variable x and reinitializes it to 25.
Function useStaticLocal (lines 53–62) declares
static variable x and initializes it to 50. Local variables
declared as static retain their values even
when they are out of scope (i.e., the function in which they are declared is not
executing). When the program calls useStaticLocal, the function prints x, increments it and prints it again before the function
returns program control to its caller. In the next call to this function,
static local variable x
contains the value 51. The initialization in line 55 occurs only once—the first
time useStaticLocal is called.
Function useGlobal (lines
65–70) does not declare any variables. Therefore, when it refers to variable
x, the global x (line 11, preceding main) is used. When the program calls useGlobal,
the function prints the global variable x, multiplies it by 10 and prints it again before the
function returns program control to its caller. The next time the program calls
useGlobal, the global variable has
its modified value, 10. After executing functions useLocal,
useStaticLocal and useGlobal twice
each, the program prints the local variable x in main again to show that none of the function calls modified the
value of x in main,
because the functions all referred to variables in other scopes.