Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
FIGURE 14.1
Program Scope
The hierarchy of the
four scopes.
File Scope
Function Scope
Block
Scope
18 067231861x CH14 4.10.2000 11:03 AM Page 233
Understanding Scope and Storage Classes
233
The
register
Specifier
The word
register
is borrowed from computer hardware terminology. Each computer has a certain number of registers to hold data and perform arithmetic or logical calculations.
Because registers are located within the CPU (central processing unit) chip, it’s much quicker to access a register than a memory location which resides outside the chip.
Therefore, storing variables in registers might help to speed up your program.
The C language provides you with the register specifier. You can apply this specifier to variables when you think it’s necessary to put the variables into the computer registers.
However, the register specifier only gives the compiler a suggestion. In other words, a variable specified by the register keyword is not guaranteed to be stored in a register.
The compiler can ignore the suggestion if there is no register available, or if some other restrictions apply.
It’s illegal to take the address of a variable that is declared with the register specifier because the variable is intended to be stored in a register, not in memory. A CPU register does not have a memory address that you can access.
In the following portion of code, the integer variable i is declared with the register specifier:
int main()
{
/* block scope with the register specifier */
register int i;
. . .
for (i=0; i
/* some statements */
}
. . .
return 0;
}
The declaration of i suggests that the compiler stores the variable in a register. Because i is intensively used in the for loop, storing i in a register might increase the speed of the code shown here.
The
extern
Specifier
As introduced in the section titled “Program Scope,” earlier in this hour, a variable with program scope is visible through all source files that make up an executable program. A
14
variable with program scope is also called a
global variable
.
Here is a question: How can a global variable declared in file A be seen in file B? In other words, how does the compiler know that the variable used in file B is actually the same variable declared in file A?
18 067231861x CH14 4.10.2000 11:03 AM Page 234
234
Hour 14
The solution is to use the extern specifier provided by the C language to allude to a global variable defined elsewhere. In this case, you declare a global variable in file A, and then declare the variable again using the extern specifier in file B. This isn’t a separate declaration, but specifies the original declaration in file A.
For instance, suppose you have two global int variables, y and z, that are defined in one file, and then, in another file, you might have the following declarations: int x = 0; /* a global variable */
extern int y; /* an allusion to a global variable y */
int main()
{
extern int z; /* an allusion to a global variable z */
int i; /* a local variable */
.
.
.
return 0;
}
As you can see, there are two integer variables, y and z, that are declared with the extern specifier, both outside and inside the main() function, respectively. When the compiler sees the two declarations, it knows that the declarations are actually allusions to the global variables y and z that are defined elsewhere.
To make your program portable across different computer platforms, you
can apply the following rules in your program when you declare or allude to global variables:
• You can ignore the extern specifier, but include an initializer, when you declare a global variable.
• You should use the extern specifier (without an initializer) when you allude to a global variable defined elsewhere.
The Storage Class Modifiers
Besides the four storage class specifiers introduced in the previous sections, C also provides you with two storage class modifiers (or
qualifiers
, as they’re sometimes called) that you can use to indicate to the C compiler how variables might be accessed.
The
const
Modifier
If you declare a variable with the const modifier, the content of the variable cannot be changed after it is initialized.
18 067231861x CH14 4.10.2000 11:03 AM Page 235
Understanding Scope and Storage Classes
235
For instance, the following expression indicates to the compiler that circle_ratio is a variable whose value should not be changed:
const double circle_ratio = 3.141593;
Likewise, the value of the character array str declared in the following statement cannot be changed, either:
const char str[] = “A string constant”;
Therefore, it’s illegal to do something like this:
str[0] = ‘a’; /* It’s not allowed here. */
In addition, you can declare a pointer variable with the const modifier so that an object pointed to by the pointer cannot be changed. For example, consider the following pointer declaration with the const modifier:
char const *ptr_str = “A string constant”;
After the initialization, you cannot change the content of the string pointed by the pointer ptr_str. For instance, the following statement is not allowed:
*ptr_str = ‘a’; /* It’s not allowed here. */
However, the ptr_str pointer itself can be assigned a different address of a string that is declared with char const.
The
volatile
Modifier
Sometimes, you want to declare a variable whose value can be changed without any explicit assignment statement in your program. This is especially true when you are dealing directly with hardware. For instance, you might declare a global variable that contains characters entered by the user. The address of the variable is passed to a device register that accepts characters from the keyboard. However, when the C compiler optimizes your program automatically, it intends to not update the value held by the variable unless the variable is on the left side of an assignment operator (=). In other words, the value of the variable is likely not changed even though the user is typing in characters from the keyboard.
To ask the compiler to turn off certain optimizations on a variable, you can declare the variable with the volatile specifier. For instance, in the following code portion, a variable, keyboard_ch, declared with the volatile specifier, tells the compiler not to opti-14
mize any expressions of the variable because the value saved by the variable might be changed without execution of any explicit assignment statement:
void read_keyboard()
{
18 067231861x CH14 4.10.2000 11:03 AM Page 236
236
Hour 14
volatile char keyboard_ch; /* a volatile variable */
.
.
.
}
Summary
In this lesson you learned the following important concepts about scopes and storage classes in C:
• A variable declared within a block has block scope. Such a variable is also called a local variable and is only visible within the block.
• A goto label has function scope, which means that it is visible through the whole block of the function within which the label is placed. No two goto labels share the same name within a function block.
• A variable declared with the static specifier outside a function has file scope, which means that it is visible throughout the entire source file in which the variable is declared.
• A variable declared outside a function is said to have program scope. Such a variable is also called a global variable. A global variable is visible in all source files that make up an executable program.
• A variable with block scope has the most limited visibility. On the other hand, a variable with program scope is the most visible, and can be seen through all files, functions, and other blocks that make up the program.
• The storage class of a variable refers to the combination of its spatial and temporal regions (that is, its scope and duration.)
• By default, a variable with block scope has an auto duration, and its memory storage is temporary.
• A variable declared with the static specifier has permanent memory storage, even after the function in which the variable is declared has been called and the function scope has exited.
• A variable declared with the register specifier might be stored in a register to speed up the performance of a program; however, the compiler can ignore the specifier if there is no register available or if some other restrictions apply.
• You can also allude to a global variable defined elsewhere by using the extern specifier from the current source file.
18 067231861x CH14 4.10.2000 11:03 AM Page 237
Understanding Scope and Storage Classes
237
• To make sure the value saved by a variable cannot be changed, you can declare the variable with the const modifier.
• If you want to let the compiler know that the value of a variable can be changed without an explicit assignment statement, declare the variable with the volatile modifier so that the compiler will turn off optimizations on expressions involving the variable.
In the next lesson you’ll learn about function declarations and prototypes in C.
Q&A
Q Can a global variable be hidden by a local variable with block scope?
A
Yes. If a local variable shares the same name with a global variable, the global variable can be hidden by the local variable for the scope of the block within which the local variable is defined with block scope. However, outside the block, the local variable cannot be seen, but the global variable becomes visible again.
Q Why do you need the
static
specifier?
A
In many cases, the value of a variable is needed, even if the scope of the block, in which the variable is declared, has exited. By default, a variable with block scope has a temporary memory storage—that is, the lifetime of the variable starts when the block is executed and the variable is declared, and ends when the execution of that block is finished. Therefore, to declare a variable with permanent duration, you have to use the static specifier to indicate to the compiler that the memory location of the variable and the value stored in the memory location should be retained after the execution of the block.
Q Does using the
register
specifier guarantee to improve the performance of a
program?
A
Not really. Declaring a variable with the register specifier only suggests to the compiler that the variable should be stored in a register. But there is no guarantee that the variable
will
be stored in a register. The compiler can ignore the request based on the availability of registers or other restrictions.
Q When you declare a variable with the
extern
specifier, do you define the variable or allude to a global variable elsewhere?
A
When a variable is declared with the extern specifier, the compiler considers the
14
declaration of the variable as an allusion rather than a definition. The compiler will therefore look somewhere else to find a global variable to which the variable with extern alludes.
18 067231861x CH14 4.10.2000 11:03 AM Page 238
238
Hour 14
Workshop
To help solidify your understanding of this lesson, you are encouraged to answer the quiz questions and finish the exercises provided in the workshop before you move to the next lesson. The answers and hints to the questions and exercises are given in Appendix B,
“Answers to Quiz Questions and Exercises.”
Quiz
1. Given the following code portion, which variables are global variables, and which ones are local variables with block scope?
int x = 0;
float y = 0.0;
int myFunction()
{
int i, j;
float y;
. . .
{
int x, y;
. . .
}
. . .
}
2. When two variables with the same name are defined, how does the compiler know which one to use?
3. Identify the storage class of each declaration in the following code portion: int i = 0;
static int x;
extern float y;
int myFunction()
{
int i, j;
extern float z;
register long s;
static int index;
const char str[] = “Warning message.”;
. . .
}
4. Given the following declaration:
const char ch_str[] = “The const specifier”;
is the ch_str[9] = ‘-’; statement legal?
18 067231861x CH14 4.10.2000 11:03 AM Page 239
Understanding Scope and Storage Classes
239
Exercises
1. Given the following:
• An int variable with block scope and temporary storage
• A constant character variable with block scope
• A float local variable with permanent storage
• A register int variable
• A char pointer initialized with a null character
write declarations for all of them.
2. Rewrite the program in Listing 14.2. This time, pass the int variable x and the float variable y as arguments to the function_1() function. What do you get on your screen after running the program?
3. Compile and run the following program. What do you get on the screen, and why?
#include
int main()
{
int i;
for (i=0; i<5; i++){
int x = 0;
static int y = 0;
printf(“x=%d, y=%d\n”, x++, y++);