Scope Rules
The scope of an identifier is the portion of the program in which the identifier can be referenced.
For example, when we define a local variable in a block, it can be referenced only following its definition in that block or in blocks nested within that block. The four identifier scopes are functioned scope, file scope, block scope, and function-prototype scope.
Labels (an identifier followed by a colon such as a 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 switch statements (as case labels) and in goto statements. Labels are implementation details that functions hide from one another. This hiding more formally called information hiding is a means of implementing the principle of least privilege, one of the most fundamental principles of good software engineering. An identifier declared outside any function has to file scope. Such an identifier is “known” (i.e., accessible) in all functions from the point at which the identifier is declared until the end of the file. Global variables, function definitions, and function prototypes placed outside a function all have file scope.
Identifiers defined inside a block have block scope. Block scope ends at the terminating right brace (}) of the block. Local variables defined at the beginning of a function have to block scope as do function parameters, which are considered local variables by the function. Any block may contain variable definitions. 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.
This means that 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. Thus, storage duration does not affect the scope of an identifier. The only identifiers with the 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. If a name is used in the parameter list of a function prototype, the compiler ignores the name. Identifiers used in a function prototype can be reused elsewhere in the program without ambiguity.
Avoid variable names that hide names in outer scopes. This can be accomplished simply by avoiding the use of duplicate identifiers in a program.Following Example demonstrates scoping issues with global variables, automatic local variables, and static local variables. A global variable x is defined and initialized to 1.This global variable is hidden in any block (or function) in which a variable named x is defined. In main, a local variable x is defined and initialized to 5. This variable is then printed to show that the global x is hidden in main. Next, a new block is defined in main with another local variable x initialized to 7.
This variable is printed to show that it hides x in the outer block of main. The variable x with value 7 is automatically destroyed when the block is exited, and the local variable x in the outer block of main is printed again to show that it’s no longer hidden. The program defines three functions that each take no arguments and return nothing. Function useLocal defines an automatic variable x and initializes it to 25. When useLocal is called, the variable is printed, incremented, and printed again before exiting the function. Each time this function is called, automatic variable x is reinitialized to 25. Function use static local defines a static variable x and initializes it to 50. Local variables declared as static retain their values even when they’re out of scope. When using static local is called, x is printed, incremented, and printed again before exiting the function. In the next call to this function, static local variable x will contain the value 51.
Function useGlobal does not define any variables. Therefore, when it refers to variable x, the global x is used. When useGlobal is called, the global variable is printed, multiplied by 10, and printed again before exiting the function. The next time function useGlobal is called, the global variable still has its modified value, 10. Finally, the program prints the local variable x in main again to show that none of the function calls modified the value of x because the functions all referred to variables in other scopes.
Example
/* A scoping example */
#include <stdio.h>
void useLocal ( void ); /*function prototype*/
void useStaticLocal ( void ); /*function prototype*/
void useGlobal ( void ); /*function prototype */
int x = 1; /* global variable*/
/*function main begins program execution */
int main( void )
{
int x = 5;
/* local variable to main*/
printf("local x in outer scope of main is %d\n", x );
.
{ /* start new scope */
int x = 7;
/* local variable to new scope */
printf( "local x in inner scope of main is %d\n", x );
} /* end new scope */
.
printf( "local x in outer scope of main is %d\n", x );
useLocal(); /* useLocal has automatic local x */
useStaticLocal(); /* useStaticLocal has static local x */
useGlobal(); /* useGlobal uses global x */
useLocal(); /* useLocal reinitializes automatic local x */
useStaticLocal(); /* static local x retains its prior value */
useGlobal(); /* global x also retains its value */
printf( "\nlocal x in main is %d\n", x );
return 0; /* indicates successful termination */
.
}
/* end main */
/* useLocal reinitializes local variable x during each call */
void useLocal( void )
{
int x = 25; /* initialized each time useLocal is called */
printf( "\nlocal x in useLocal is %d after entering useLocal\n", x );
x++;
printf( "local x in useLocal is %d before exiting useLocal\n", x );
}
/* end function useLocal */
/* useStaticLocal initializes static local variable x only the first time
the function is called; value of x is saved between calls to this
function */
void useStaticLocal( void )
{
/* initialized only first time useStaticLocal is called */
static int x = 50;
printf( "\nlocal static x is %d on entering useStaticLocal\n", x );
x++;
printf( "local static x is %d on exiting useStaticLocal\n", x );
} /* end function useStaticLocal */
/* function useGlobal modifies global variable x during each call */
void useGlobal( void )
{
printf( "\nglobal x is %d on entering useGlobal\n", x );
x *= 10;
printf( "global x is %d on exiting useGlobal\n", x );
} /* end function useGlobal */
OUTPUT:
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 use local is 25 after entering use local
local x in use local is 26 before exiting use local
local static x is 50 on entering use static local
local static x is 51 on existing use static local
global x is 1 on entering use global
global x is 10 on existing use global
local x in use local is 25 after entering use local
local x in use local is 26 before exiting use local
local static x is 51 on entering use static local
local static x is 52 on existing use static local
global x is 10 on entering use global
global x is 100 on existing use global
local x in main is 5