Storage Classes In C

"Functions" you saw that a variable defined within a function is different from a variable defined outside a function. Without knowing it, you were being introduced to the concept of variable scope, an important aspect of C programming. Today you will learn

  • About scope and why it's important
  • What external variables are and why you should usually avoid them
  • The ins and outs of local variables
  • The difference between static and automatic variables
  • About local variables and blocks
  • How to select a storage class

What Is Scope?

The scope of a variable refers to the extent to which different parts of a program have access to the variable--in other words, where the variable is visible. When referring to C variables, the terms accessibility and visibility are used interchangeably. When speaking about scope, the term variable refers to all C data types: simple variables, arrays, structures, pointers, and so forth. It also refers to symbolic constants defined with the const keyword.

Scope also affects a variable's lifetime: how long the variable persists in memory, or when the variable's storage is allocated and deallocated. First, this chapter examines visibility.

A Demonstration of Scope

Look at the program in Listing 1. It defines the variable x in line 5, uses printf() to display the value of x in line 11, and then calls the function print_value() to display the value of x again. Note that the function print_value() is not passed the value of x as an argument; it simply uses x as an argument to printf() in line 19.

Program 1. The variable x is accessible within the function print_value().

// Illustrates variable scope. 
// The variable x is accessible within the function print_value().
#include<stdio.h>

int x = 999;
void print_value(void);

int main() {
  printf("%d\n", x);
  print_value();
  return 0;
}

//function print_value().

void print_value(void) {
  printf("%d\n", x);
}

Sample Output:

999

999

This program compiles and runs with no problems. Now make a minor modification in the program, moving the definition of the variable x to a location within the main() function. The new source code is shown in Listing 2.

Program 2. The variable x is not accessible within the function print_value() : Error

//Illustrates variable scope.
//The variable x is not accessible within the function print_value().

#include<stdio.h>

void print_value(void);

int main() {
  int x = 999;
  printf("%d\n", x);
  print_value();
  return 0;
}

//function print_value().

void print_value(void) {
   printf("%d\n", x);
}

Why Is Scope Important?

To understand the importance of variable scope, you need to recall the discussion of structured programming on Day 5. The structured approach, you might remember, divides the program into independent functions that perform a specific task. The key word here is independent. For true independence, it's necessary for each function's variables to be isolated from interference caused by other functions. Only by isolating each function's data can you make sure that the function goes about its job without some other part of the program throwing a monkey wrench into the works.

If you're thinking that complete data isolation between functions isn't always desirable, you are correct. You will soon realize that by specifying the scope of variables, a programmer has a great deal of control over the degree of data isolation.

External Variables

An external variable is a variable defined outside of any function. This means outside of main() as well, because main() is a function, too. Until now, most of the variable definitions in this book have been external, placed in the source code before the start of main(). External variables are sometimes referred to as global variables.

External Variable Scope

The scope of an external variable is the entire program. This means that an external variable is visible throughout main() and every other function in the program. For example, the variable x in Listing 12.1 is an external variable. As you saw when you compiled and ran the program, x is visible within both functions, main() and print_value().

Strictly speaking, it's not accurate to say that the scope of an external variable is the entire program. Instead, the scope is the entire source code file that contains the variable definition. If the entire program is contained in one source code file, the two scope definitions are equivalent. Most small-to-medium-sized C programs are contained in one file, and that's certainly true of the programs you're writing now.

The extern Keyword

When a function uses an external variable, it is good programming practice to declare the variable within the function using the extern keyword. The declaration takes the form

extern type name;

in which type is the variable type and name is the variable name. For example, you would add the declaration of x to the functions main() and print_value() in Listing 1. The resulting program is shown in Listing 3.

Program 3. The external variable x is declared as extern within the functions main() and print_value().

//Illustrates declaring external variables.
//The external variable x is declared as extern within the functions main() and print_value().

#include<stdio.h>

int x = 999;
void print_value(void);

int main() {
  extern int x;
  printf("%d\n", x);
  print_value();
  return 0;
}

//function print_value().

void print_value(void) {
  //extern x
  extern int x;
  printf("%d\n", x);
}

Sample Output:

999

999

Local Variables

local variable is one that is defined within a function. The scope of a local variable is limited to the function in which it is defined. Day 5 describes local variables within functions, how to define them, and what their advantages are. Local variables aren't automatically initialized to 0 by the compiler. If you don't initialize a local variable when it's defined, it has an undefined orgarbage value. You must explicitly assign a value to local variables before they're used for the first time.

A variable can be local to the main() function as well. This is the case for x in Listing 2. It is defined within main(), and as compiling and executing that program illustrates, it's also only visible within main().

Static Versus Automatic Variables

Local variables are automatic by default. This means that local variables are created anew each time the function is called, and they are destroyed when execution leaves the function. What this means, in practical terms, is that an automatic variable doesn't retain its value between calls to the function in which it is defined.

Suppose your program has a function that uses a local variable x. Also suppose that the first time it is called, the function assigns the value 100 to x. Execution returns to the calling program, and the function is called again later. Does the variable x still hold the value 100? No, it does not. The first instance of variable x was destroyed when execution left the function after the first call. When the function was called again, a new instance of x had to be created. The old x is gone.

What if the function needs to retain the value of a local variable between calls? For example, a printing function might need to remember the number of lines already sent to the printer to determine when a new page is needed. In order for a local variable to retain its value between calls, it must be defined as static with the static keyword. For example:

void func1(int x)
{
	static int a;
	/* Additional code goes here */
}

Listing 4 illustrates the difference between automatic and static local variables.

Listing 4. The difference between automatic and static local variables.

//The difference between automatic and static local variables.
//Demonstrates automatic and static local variables.

#include<stdio.h>

void func1(void);

int main() {
  int count;
  for (count = 0; count < 20; count++) {
    printf("At iteration %d: ", count);
    func1();
  }
  return 0;
}

void func1(void) {
  static int x = 0;
  int y = 0;
  printf("x = %d, y = %d\n", x++, y++);
}

Sample Output:

At iteration 0: x = 0, y = 0

At iteration 1: x = 1, y = 0

At iteration 2: x = 2, y = 0

At iteration 3: x = 3, y = 0

At iteration 4: x = 4, y = 0

At iteration 5: x = 5, y = 0

At iteration 6: x = 6, y = 0

At iteration 7: x = 7, y = 0

At iteration 8: x = 8, y = 0

At iteration 9: x = 9, y = 0

At iteration 10: x = 10, y = 0

At iteration 11: x = 11, y = 0

At iteration 12: x = 12, y = 0

At iteration 13: x = 13, y = 0

At iteration 14: x = 14, y = 0

At iteration 15: x = 15, y = 0

At iteration 16: x = 16, y = 0

At iteration 17: x = 17, y = 0

At iteration 18: x = 18, y = 0

At iteration 19: x = 19, y = 0

The Scope of Function Parameters

A variable that is contained in a function heading's parameter list has local scope. For example, look at the following function:

void func1(int x)
{
	int y;
	/* Additional code goes here */
}

Both x and y are local variables with a scope that is the entire function func1(). Of course, x initially contains whatever value was passed to the function by the calling program. Once you've made use of that value, you can use x like any other local variable.

Because parameter variables always start with the value passed as the corresponding argument, it's meaningless to think of them as being either static or automatic.

External Static Variables

You can make an external variable static by including the static keyword in its definition:

static float rate;
main()
{
    /* Additional code goes here */
}

The difference between an ordinary external variable and a static external variable is one of scope. An ordinary external variable is visible to all functions in the file and can be used by functions in other files. A static external variable is visible only to functions in its own file and below the point of definition.

These distinctions obviously apply mostly to programs with source code that is contained in two or more files. This topic is covered on Day 21.

Register Variables

The register keyword is used to suggest to the compiler that an automatic local variable be stored in a processor registerrather than in regular memory. What is a processor register, and what are the advantages of using it?

The central processing unit (CPU) of your computer contains a few data storage locations called registers. It is in the CPU registers that actual data operations, such as addition and division, take place. To manipulate data, the CPU must move the data from memory to its registers, perform the manipulations, and then move the data back to memory. Moving data to and from memory takes a finite amount of time. If a particular variable could be kept in a register to begin with, manipulations of the variable would proceed much faster.

By using the register keyword in the definition of an automatic variable, you ask the compiler to store that variable in a register. Look at the following example:

Note that I said ask, not tell. Depending on the program's needs, a register might not be available for the variable. In this case, the compiler treats it as an ordinary automatic variable. The register keyword is a suggestion, not an order. The benefits of the register storage class are greatest for variables that the function uses frequently, such as the counter variable for a loop.

The register keyword can be used only with simple numeric variables, not arrays or structures. Also, it can't be used with either static or external storage classes. You can't define a pointer to a register variable.

void func1(void)
{
    register int x;
    /* Additional code goes here */
}

Which Storage Class Should You Use?

  • The auto keyword is optional.
  • The extern keyword is used in functions to declare a static external variable that is defined elsewhere.

When you're deciding on a storage class, you should use an automatic storage class whenever possible and use other classes only when needed. Here are some guidelines to follow:

  • Give each variable an automatic local storage class to begin with.
  • If the variable will be manipulated frequently, add the register keyword to its definition.
  • In functions other than main(), make a variable static if its value must be retained between calls to the function.
  • If a variable is used by most or all of the program's functions, define it with the external storage class.