Storage Classes and Scope

I l @ ve RuBoard

Storage Classes and Scope

We gave storage classes a passing mention in Chapter 10, "Arrays and Pointers," but let's take a more in-depth look now. As mentioned in Chapter 10, local variables are known only to the functions containing them. However, C also offers the possibility of global variables , those known to several functions. Suppose you want both main() and critic() to have access to the variable Units . You can do this by declaring Units so that it belongs to the external storage class, as shown in Listing 13.1. (Here we follow a common, but not universal, convention of uppercasing the first character of a global variable name .)

Listing 13.1 The global.c program.
 /* global.c -- uses an external variable */ #include <stdio.h> int Units;                /* an external variable      */ void critic(void); int main(void) {    extern int Units;      /* an optional redeclaration */    printf("How many pounds to a firkin of butter?\n");    scanf("%d", &Units);    while ( Units != 56)        critic();    printf("You must have looked it up!\n");    return 0; } void critic(void) {    /* optional redeclaration omitted */    printf("No luck, chummy. Try again.\n");    scanf("%d", &Units); } 

Here is a sample output:

 How many pounds to a firkin of butter? 14 No luck, chummy. Try again. 56 You must have looked it up! 

(We did.)

Note how the second value for Units was read by the critic() function, yet main() also knew the new value when it finished the while loop. We made Units an external variable by defining it outside of (external to) any function definition. That's all you need to do to make Units available to all the subsequent functions in the file.

There are three ways to treat Units within a function. The simplest is to have no further declarations. In that case, the function uses the externally defined Units by default; critic() does this.

The second way, and the one main() took, is to place the following declaration in the function:

 extern int Units; 

In the example, inserting this declaration is mainly a matter of documentation. It tells the compiler that any mention of Units in this particular function refers to a variable defined outside the function, perhaps even outside the file. Again, both functions use the externally defined Units .

The third way, and one quite different in meaning, is to use this declaration in the functions:

 int Units; 

Suppose you do this for main() but not for critic() . Omitting the keyword extern causes the compiler to create a separate variable private to main() , but also named Units . The new Units would be visible while main() is running, but when control passes to critic() , the program would interpret Units as the externally defined one, and the value assigned to Units in main() would not affect the value of Units in critic() .

Each variable, as you know, has a type. In addition, each variable has a storage class. There are four keywords used to describe storage classes: extern (for external), auto (for automatic), static , and register . Variables declared within a function are considered to be class auto unless declared otherwise . The formal arguments to functions normally are class auto , but they can be made class register .

A variable's storage class is determined by where it is defined and by what keyword, if any, is used. The storage class determines three things. First, it controls which functions in a file have access to a variable. If a section of code can use a particular variable, we say the variable is visible in that section. A variable's visibility to the parts of a program is its scope . Second, the storage class determines in how many places the same variable can be declared; this property is described by a variable's linkage . Finally, the storage class specifies how long the variable persists in memory ”its storage duration . Let's look at each of these terms briefly , then see how they relate to the different storage types.

Scope, Linkage, and Storage Duration

A C variable has one of the following three scopes: file scope, block scope , or function prototype scope . A variable with file scope is visible from the point it is defined to the end of the file containing the definition. For instance, the variable Units in Listing 13.1 has file scope, and it's visible to all the functions following its definition.

A variable with block scope is visible from the point it is defined until the end of the block containing the definition. (A block, recall, is marked by braces.) The local variables we've used to date, including formal function arguments, have block scope. Indeed, block scope is what makes them local, confining them to the functions in which they are defined. The function body is considered to be the block containing the formal function arguments. Therefore, the variables cleo and patrick in the following code both have block scope extending to the closing brace :

 double blocky(double cleo) {      double patrick = 0.0;      ...      return patrick; } 

Function prototype scope applies to variable names used in function prototypes , as in the following:

 int mighty(int mouse, double large); 

Function prototype scope runs from the point the variable is defined to the end of the prototype declaration. What this means is that all the compiler cares about when handling a function prototype arguments are the types; the names you use, if any, don't matter.

A C variable has one of the following linkages: external linkage, internal linkage , or no linkage . A variable with external linkage can be used anywhere in a multifile program. A variable with internal linkage can be used anywhere in a single file, and a variable with no linkage can be used only in the block where it is defined. Variables with internal or external linkage can appear in more than one declaration, but variables with no linkage can be declared only once.

As you may have noticed, the concepts of linkage and scope are related . Variables with block scope or function prototype scope have no linkage, which is why the descriptions of no linkage and block scope are similar. As you'll see later, you can use the concepts of internal and external linkage to divide variables with file scope into two groups.

A C variable has one of the following two storage durations: static storage duration or automatic storage duration . If a variable has static storage duration, it exists throughout program execution. External variables, such as Units from Listing 13.1, have static storage duration. A variable with automatic storage duration is guaranteed to exist only while the program is executing the block where the variable is defined. The local variables we've used so far fall into this category. For instance, in the following code, the variables number and index come into being each time the bore() function is called and pass away each time the function completes:

 void bore(int number) {      int index;      for (index = 0; index < number; index++)           puts("They don't make them the way they used to.\n");      return 0; } 

Now let's discuss C's storage classes in more detail.

Automatic Variables

By default, variables declared in a function are automatic. You can, however, make your intentions perfectly clear by explicitly using the keyword auto , as shown here:

 int main(void) {   auto int plox; 

You might do this, for example, to document that you are intentionally overriding an external function definition or that it is important not to change the variable to another storage class.

An automatic variable has block scope. Only the function in which the variable is defined can access that variable by name. (Of course, arguments can be used to communicate the variable's value and address to another function, but that is indirect knowledge.) Another function can use a variable with the same name, but it will be an independent variable stored in a different memory location.

An automatic variable has no linkage. You can't declare it twice in the same block. You can declare variables having the same name but existing in different blocks, but they would be separate, independent variables with no links between them.

An automatic variable has automatic storage duration. It comes into existence when the function that contains it is called. When the function finishes its task and returns control to its caller, the automatic variable disappears. Its memory location can now be used for something else.

One more point about the scope of an automatic variable: Its scope is confined to the block (paired braces) where the variable is declared. We have always declared variables at the beginning of the function block, so the scope is the whole function, but in principle you could declare a variable within a sub-block. Then that variable would be known only to that subsection of the function. Here's an example:

 int loop(int n) {      int m;      scanf("%d", &m);      {           int i;    /* local to this sub-block */           for (i = m; i < n; i++)                puts("i is local to a sub-block\n");      }      return m; } 

In this code, i is visible only within the inner braces. You'd get a compiler error if you tried to use it before or after the inner block. Normally, you wouldn't use this feature when designing a program. Sometimes, however, it is useful to define a variable in a sub-block if it is not used elsewhere. In that way, you can document the meaning of a variable close to where it is used. Also, the variable doesn't sit unused, occuping memory when it is no longer needed.

What if you reuse a name? Then the name defined inside the block is the variable used inside the block. We say it hides the outer definition, but when execution exits the inner block, the outer variable comes back into scope. Listing 13.2 illustrates these points and more.

Listing 13.2 The hiding.c program.
 /* hiding.c -- variables in blocks */ #include <stdio.h> int main() {     int x = 30;     printf("x in outer block: %d\n", x);     {         int x = 77;  /* new x, hides first x */         printf("x in inner block: %d\n", x);     }     printf("x in outer block: %d\n", x);     while (x++ < 33)     {         int x = 100; /* new x, hides first x */         x++;         printf("x in while loop: %d\n", x);     }     return 0; } 

Here's the output:

 x in outer block: 30 x in inner block: 77 x in outer block: 30 x in while loop: 101 x in while loop: 101 x in while loop: 101 

First, the program creates an x with the value 30 , as the first printf() statement shows. Then it defines a new x with the value of 77 , as the second printf() statement shows. That it is a new variable hiding the first x is shown by the third printf() statement. It is located after the first inner block, and it displays the original x value, showing that the original x variable never went away and never got changed.

Perhaps the most intriguing part of the program is the while loop. The while loop test uses the original x :

 while(x++ < 33) 

Inside the loop, however, the program sees a third x variable, one defined just inside the while loop block. So when the code uses x++ in the body of the loop, it is the new x that is incremented to 101 , and then displayed. When each loop cycle is completed, that new x disappears. Then the loop test condition uses and increments the original x , the loop block is entered again, and the new x is created again. In this example, that x is created and destroyed three times. Note that, to terminate, this loop had to increment x in the test condition because incrementing x in the body increments a different x than the one used for the test.

The intent of this example is not to encourage you to write code like this. Rather, it is to illustrate what happens when you define variables inside a block.

Initialization of Automatic Variables

Automatic variables are not initialized unless you do so explicitly. Consider the following declarations:

 int main(void) {   int repid;   int tents = 5; 

The tents variable is initialized to 5 , but the repid variable winds up with whatever value happened to previously occupy the space assigned to repid . You cannot rely on this value being .

External Variables

A variable defined outside a function is external. As a matter of documentation, an external variable can also be declared inside a function that uses it by using the extern keyword. If the variable is defined in another file, declaring the variable with extern is mandatory. Declarations look like this:

 int Errupt;           /* externally defined variable    */ double Up[100];       /* externally defined array       */ extern char Coal;     /* mandatory declaration          */                       /* Coal defined in another file   */ void next(void); int main(void) {   extern int Errupt;  /* optional declaration           */   extern double Up[]; /* optional declaration           */   ... } void next(void) {   ... } 

The two declarations of Errupt are examples of linkage, for they both refer to the same variable. External variables have external linkage, a point we'll return to later.

Note that you don't have to give the array size in the optional declaration of double Up . That's because the original declaration already supplied that information. The group of extern declarations inside main() can be omitted entirely because external variables have file scope, so they are known from the point of declaration to the end of the file.

If only the extern is omitted from the declaration inside a function, then a separate automatic variable is set up. That is, replacing

 extern int Errupt; 

with

 int Errupt; 

in main() causes the compiler to create an automatic variable named Errupt . It would be a separate, local variable, distinct from the original Errupt . The local variable would be in scope while the program executes main() , but the external Errupt would be in scope for other functions, such as next() , in the same file. In short, a variable in block scope "hides" a variable of the same name in file scope while the program executes statements in the block.

External variables have static storage duration. Therefore, the array double_Up maintains its existence and values regardless of whether the program is executing main() , next() , or some other function.

The following three examples show four possible combinations of external and automatic variables. In Example 1, there is one external variable: Hocus . It is known to both main() and magic() .

 /* Example 1 */ int Hocus; int magic(); int main(void) {    extern int Hocus;  /* Hocus declared external */    ... } int magic() {    extern int Hocus;    ... } 

Example 2 has one external variable, Hocus , known to both functions. This time, magic() knows it by default.

 /* Example 2 */ int Hocus; int magic(); int main(void) {    extern int Hocus;  /* Hocus declared external */    ... } int magic() {                        /* Hocus not declared at all */    ... } 

In Example 3, four separate variables are created. The Hocus in main() is automatic by default and is local to main . The Hocus in magic() is automatic explicitly and is known only to magic() . The external Hocus is not known to main() or magic() but would be known to any other function in the file that did not have its own local Hocus . Finally, Pocus is an external variable known to magic() but not to main() because Pocus follows main() .

 /* Example 3 */ int Hocus; int magic(); int main(void) {   int Hocus;        /* Hocus declared, is auto by default */    ... } int Pocus; int magic() {    auto int Hocus;  /* Hocus declared automatic           */    ... } 

These examples illustrate the scope of external variables. They also illustrate the lifetimes of variables. The external Hocus and Pocus variables persist as long as the program runs, and, because they aren't confined to any one function, they don't fade away when a particular function returns.

Note

If you use the keyword extern without a type, the compiler interprets it as being extern int .


Initializing External Variables

Like automatic variables, external variables can be initialized explicitly. Unlike automatic variables, external variables are automatically initialized to zero if you don't initialize them. This rule applies to elements of an externally defined array, too.

External Names

The rules for names of external variables are more restrictive than for local variables. The reason is that external names need to comply with the rules of the local environment, which may be more limiting. For local names, the ANSI C standard requires that a compiler distinguish between uppercase and lowercase and that it recognize the first 31 characters in a name. For external variables, the compiler does not need to distinguish between uppercase and lowercase, and it need recognize only the first 6 characters in a name. These rules apply to function names, too. The preceding limitations stem from the desire to make C programs compatible with some of the older, more limited operating systems and languages.

The C9X committee feels that it is now time to break the shackles of the past; it proposes that compilers recognize the first 63 characters for local identifiers and the first 31 characters for external identifiers.

Definitions and Declarations

There is a difference between defining a variable and declaring it. Consider this example:

 int tern;                /* tern defined                */ main() {      external int tern;  /* use a tern defined elsewhere */ 

Here tern is declared twice. The first declaration causes storage to be set aside for the variable. It constitutes a definition of the variable. The second declaration merely tells the compiler to use the tern variable that has been created previously, so it is not a definition. The first declaration is called a defining declaration, and the second is called a referencing declaration. The keyword extern always indicates that a declaration is not a definition because it instructs the compiler to look elsewhere.

Suppose you do this:

 extern int tern; int main(void) { 

Then the compiler assumes that the actual definition of tern is somewhere else in your program, perhaps in another file. This declaration does not cause space to be allocated. Therefore, don't use the keyword extern to create an external definition; use it only to refer to an existing external definition.

An external variable can be initialized only once, and that must occur when the variable is defined. A statement like

 extern char permis = `Y';   /* error */ 

is in error because the presence of the keyword extern signifies a referencing declaration, not a defining declaration.

Static Variables

The name static variable sounds like a contradiction, like a variable that can't vary. Actually, "static" means that the variable stays put. These variables have the same scope as automatic variables, but they don't vanish when the containing function ends its job. That is, static storage class variables have block scope, but static storage duration. The computer remembers their values from one function call to the next. The example in Listing 13.3 illustrates this point and shows how to declare a static variable.

Listing 13.3 The static.c program.
 /* static.c -- using a static variable */ #include <stdio.h> void trystat(void); int main(void) {    int count;    for (count = 1; count <= 3; count++)    {        printf("Here comes iteration %d:\n", count);        trystat();    }    return 0; } void trystat(void) {    int fade = 1;    static int stay = 1;    printf("fade = %d and stay = %d\n", fade++, stay++); } 

Note that trystat() increments each variable after printing its value. Running the program gives this output:

 Here comes iteration 1: fade = 1 and stay = 1 Here comes iteration 2: fade = 1 and stay = 2 Here comes iteration 3: fade = 1 and stay = 3 

The static variable stay remembers that its value was increased by 1, but the fade variable starts anew each time. This points out a difference in initialization: fade is initialized each time trystat() is called, but stay is initialized just once, when trystat() is compiled. Like external variables, static variables are initialized to zero if you don't explicitly initialize them to some other value.

The two declarations look similar:

 int fade = 1; static int stay = 1; 

However, the first statement is really part of the trystat() function and is executed each time the function is called. It is a runtime action. The second statement isn't actually part of the trystat() function. If you use a debugger to execute the program step-by-step, you'll see that the program seems to skip that step. That's because static variables and external variables are already in place after a program is loaded into memory. Placing the statement in the trystat() function tells the compiler that only the trystat() function is allowed to see the variable; it's not a statement that's executed during runtime.

External Static Variables

You can also declare a static variable outside all functions. This creates an external static variable. The difference between an ordinary external variable and an external static variable lies in its linkage (see Figure 13.1). The ordinary external variable can be used by functions in any file, but the external static variable can be used only by functions in the same file. In other words, an ordinary external variable has external linkage, but a static external variable has internal linkage. You set up an external static variable by placing the definition outside all functions and by using the keyword static in that definition:

Figure 13.1. External variable versus external static variable.
graphics/13fig01.jpg
 static int randx = 1; int rand() { 

Later on, Listing 13.4 shows an example with a static variable.

Multiple Files

Complex C programs often use several separate files of code. Sometimes these files might need to share an external variable. The ANSI C way to do this is to have a defining declaration in one file and referencing declarations in the other files. That is, all but one declaration (the defining declaration) should use the extern keyword, and only the defining declaration can be used to initialize the variable.

Note that an external variable defined in one file is not available to a second file unless it is also declared (by using extern ) in the second file. An external declaration by itself only makes a variable potentially available to other files.

Historically, however, many compilers have followed different rules in this regard. Many UNIX systems, for example, enable you to declare a variable in several files without using the extern keyword, provided that no more than one declaration includes an initialization. If there is a declaration with an initialization, it is taken to be the definition.

Scope and Functions

Functions, too, have storage classes. A function can be either external (the default) or static. An external function can be accessed by functions in other files, but a static function can be used only within the defining file. Consider, for example, a file containing these function declarations:

 double gamma();         /* external by default */ static double beta(); extern double delta(); 

The functions gamma() and delta() can be used by functions in other files that are part of the program, but beta() cannot. Because this beta() is restricted to one file, you can use a different function having the same name in the other files. One reason to use the static storage class is to create functions that are private to a particular module, thereby avoiding the possibility of name conflicts.

We recommend that you use the extern keyword when declaring functions defined in other files. This practice is mostly a matter of clarity because a function declaration is assumed to be extern unless the keyword static is used.

Register Variables

Variables are normally stored in computer memory. With luck, register variables are stored in the CPU registers or, more generally , in the fastest memory available, where they can be accessed and manipulated more rapidly than regular variables. In other respects, register variables are the same as automatic variables. They are declared this way:

 int main(void) {    register int quick; 

We say "with luck" because declaring a variable as register class is more a request than a direct order. The compiler has to weigh your demands against the number of registers or amount of fast memory available, so you might not get your wish. In that case, the variable becomes an ordinary automatic variable.

You can request that formal parameters be register variables. Just use the keyword in the function heading:

 void macho(register int n) 

The types that can be declared register may be restricted. For example, the registers in a processor might not be large enough to hold type double . Also, the & operator does not work with register variables.

Summary: Scopes

Automatic variables have block scope, no linking, and automatic storage duration. They are local and private to the block (typically a function) where they are defined. When a variable is declared external to any function in a file, it's an external variable and has file scope, external linking, and static storage duration. An external variable will be known to all functions following it in that file, whether or not those functions explicitly declare the variable as extern . If you want a function in a second file to access an external variable in the first file, you must declare the variable in the second file by using the keyword extern .

A static variable is defined inside a function by using the keyword static . It is local to that function. It has block scope, no linkage, and static storage duration. A static external variable is defined outside a function by using the keyword static . It has file scope, internal linking, and static storage duration. A static external variable is local to its file.

Which Storage Class?

The answer to the question "Which storage class?" is most often "automatic." After all, why else was automatic selected as the default? Yes, we know that at first glance external storage is quite alluring. Just make all your variables external, and you never have to worry about using arguments and pointers to communicate between functions. There is a subtle pitfall, however. You will have to worry about function A() sneakily altering the variables used in function B() , despite your intentions to the contrary. The unquestionable evidence of untold years of collective computer experience is that this one subtle danger far outweighs the superficial attraction of using external storage indiscriminately.

One of the golden rules of protective programming is the "need to know" principle. Keep the inner workings of each function as private to that function as possible, sharing only those variables that need to be shared. The other classes are useful, and they are available. Before using one, though, ask yourself if it is necessary.

Summary: Storage Classes

Keywords:

 auto, extern, static, register 

General Comments:

The storage class of a variable determines its scope, its linkage, and its storage duration. Storage class is determined both by where the variable is defined and by its associated keyword. Variables defined outside all functions are external, have file scope, external linkage, and static storage duration. Variables declared inside a function are automatic unless one of the other keywords is used. They have block scope, no linkage, and automatic storage duration. Variables defined with the keyword static inside a function have block scope, no linkage, and static storage duration. Variables defined with the keyword static outside a function have file scope, internal linkage, and static storage duration.

Properties:

In the following list, those variables in storage classes above the dotted line are declared inside a function; those below the line are defined outside a function.

Storage Class Keyword Duration Scope and Linkage
automatic auto temporary local
register register temporary local
static static persistent local
external extern * persistent global (all files)
external static persistent global (one file)
static      
* The keyword extern is used only to redeclare variables that have been defined externally elsewhere. The act of defining the variable outside a function makes it external .
I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net