Every variable has a type associated with it which decides the values that can be assigned to it and the operations that can be performed on it. In addition, we can specify a storage class for a variable which decides the following:
1. The type of storage to be used (either memory or CPU registers)
2. The scope or the part of the program from which that variable can be accessed
3. The lifetime, i. e., how long the variable will continue to live or exist
4. The default initial value, i. e., the value assigned to the variable if it is not explicitly initialized.
The C language provides four storage classes, namely, automatic, register, static and external. The keywords for these storage classes are auto, register, static and extern, respectively. The characteristics of the storage classes are summarized in Table
Storage class | Storage | Scope | Lifetime | Default initial value |
Register | Local to block in which variable is defined | Till control remains within the block in which variable is defined | Garbage | |
Automatic | ||||
Static | ||||
External | Global | Till the end of program execution | 0 for arithmetic types; NULL for pointers |
A storage class is specified for a variable using the general format shown below.
storage_class data_type varl, var2, …;
The order of the storage class and data type can be altered and the int data type can be omitted. Consider the example given below.
auto int a, b;
register int i, j;
static float p, q;
extern char x, y;
This example declares a and b as automatic variables of type int, i and j as register variables of type int, p and q as static variables of type float and x and y as external variables of type char.
Note that if we do not specify the storage class for a variable, the defaults are applicable. Thus, a variable defined within a block without any storage class is an automatic variable and a variable defined outside any function without any storage class is an external variable. We can use any storage class while defining a variable within a block. However, for the variables defined outside any function, the storage class can be static or be omitted. The compiler will give an error if we use auto or register class for such variables defined outside any function.
We’ll be covering the following topics in this tutorial:
Automatic Storage Class
As mentioned earlier, if we do not specify a storage class while defining a variable inside a block, the automatic storage class is assumed. However, we can use the auto keyword to explicitly specify the automatic storage class for such variables.
As we know, automatic variables are not initialized to any specific value by default. Thus, if we do not initialize an automatic variable when it is defined, it will have a garbage value, i. e., a random, unpredictable value which is likely to change each time the program is executed. Automatic variables are stored in memory. Their scope is local to the block in which they are defined, from the point of definition to the end of that block. Thus, once a variable is defined, it can be used in any subsequent part of that block including the blocks nested within it. However, it cannot be used outside that block.
An automatic variable is created every time control enters the block in which it is defined and it lives until control remains in that block. It is destroyed when control leaves that block. Note that irrespective of the storage class used, the variable names must be unique within a block. Thus, we cannot redefine a variable within the same block with a different type and/or storage class. However, it is possible to reuse that variable name for other variables of any data type and/or storage class defined within other blocks including the contained blocks. When we access such a variable from within a block, the local variables (i. e., the variables defined in that block) are given preference. However, if the accessed variable is not defined in that block, variable access refers to the definition in the nearest outer block in which this block is contained. To understand this concept clearly, study the example given below.
int main()
{
int x = 1;
clrscr();
printf(“%d “, x); /* prints 1 */
{
printf(“%d “, x); /* prints 1 */
{
int x = 2; /* variable name x reused */
printf(“%d “, x); /* prints 2 */
}
printf(“%d “, x); /* prints 1 */
}
printf(“%d “, x); /* prints 1 */
return 0;
}
The comments after the printf statements specify the values printed by these statements. The code is easy to understand and its output is given below.
1 1 2 1 1
Now consider another example given below
void show_result(int marks)
{
char result[10];
if (marks >= 40)
{
char result[] = “Pass”;
}
else {
char result[] = “Fail”;
printf(“Result: %s”, result);
}
printf(“Result: %s”, result);
}
In this function, the variable result is first declared as a char array. Then this variable is reused to define and initialize two char arrays (with values “Pass” and “Fail”)within the if block and else block of an if-else statement. Depending on the outcome of the condition in the if statement, one of these arrays will be created. However, it will be destroyed as soon as control leaves that block. Thus, the printf statement will print the value of the result array defined in the beginning of this function. As this is an automatic array, its elements are not initialized with any specific value and thus have garbage values. When this function was called in Code::Blocks (with value of marks as 50), the output was displayed as shown below.
Register Storage Class
CPU registers are much faster than memory. Thus, if we store frequently used variables in CPU registers, the program will run faster. We can request the C compiler to store one or more variables in CPU registers by specifying the register storage class when these variables are defined. However, note that a CPU has a limited number of registers. Hence, it may not be possible for the compiler to honor all requests for allocation of CPU registers. If the compiler is not in a position to allocate a CPU register for a requested variable, that variable is stored in memory and is treated as an automatic variable.
Apart from the storage location, register variables are similar to automatic variables. Thus, they have scope local to the block in which they are defined, they are destroyed when control leaves that block and the compiler does not initialize them with any specific values (they have garbage values), if we do not explicitly initialize them.
The register storage class should be used for frequently used variables, e. g., control variables in loops. Consider the example given below.
int sum(int n)
{
register int i, sum;
sum = 0;
for(i = 1; i <= n; i++)
sum += i·;
return sum;
}
This code segment defines variables i as well as sum as register variables in an attempt to speed up program execution.
Note that whether a variable will be stored in a CPU register or not depends, besides the availability of a CPU register, on the possibility of accommodating that variable in the CPU register. As modem CPUs have 64 bit registers, it is possible to allocate CPU registers for variables of basic data types (char, int, float and double) as well as pointer variables. Remember that we should not request a register storage class for bigger data objects such as arrays or large structures. However, if we do so, the compiler will ignore the request and treat those variables as automatic variables, as mentioned earlier:
Static Storage Class
Like automatic variables, static variables are also stored in memory and have local scope. However, they differ in two respects, lifetime and default initial values.
Static variables come into existence when the execution of the block in which they are defined begins for the first time and they are not destroyed when control leaves that block. Thus, they live till the end of program execution. Note that the values assigned to these variables are available the next time that block is executed. Thus, static variables can be used to remember data values in a function across function calls.
If we do not explicitly initialize a static variable when it is defined, the compiler initializes it with the default initial value, which is zero for arithmetic types and NULL for pointers. This initialization is done only once, the first time control reaches that block. Initialization is not done in subsequent executions of that block.
To further understand the concept of static variables, consider the program given below.
#include <stdio.h>
void func(void)
int main ()
{
func () ;
func ();
func ();
return 0;
}
void func (void)
{
static int x = 100;
printf(“%d “, x++);
}
In this example, the function func is called three times from the main function. This function defines a static variable x with initial value 100. This value is printed and then incremented in a printf statement.
The first time the function func is called, variable x is initialized to 100. The printf statement prints this value and increments it to l 01. When control leaves function func,x isnot destroyed as it is a static variable and its value is preserved for use in subsequent calls to the function.
When the function func is called a second time, x is not initialized again. Thus, the printf statement prints the value of x (101) and increments it to 102. Finally, the third call to function func prints the value 102 (and increments it to 103). The output of the program is given below.
101 102 103
What would program output be if we declare variable x in function func as shown below?
static int x;
External Storage Class
External variables, as the name indicates, are defined outside any function. These variables are similar to static variables in terms of storage location, lifetime and default initial values.
External variables are stored in memory. They come to existence when program execution begins and live till the end of program execution. Also, the compiler initializes them with default initial values (0 for arithmetic types and NULL for pointers), if they are not explicitly initialized.
However, unlike all other variables (automatic, register and static which have local scope), external variables have global scope. Hence, these variables are also called global variables. They can be accessed in all parts of the program including all the functions in the same program file as well as other program files in a multi-file C program. Thus, they provide an easy mechanism to share data between different functions without explicitly passing them around. This is particularly useful if a large number of variables are shared by multiple functions. It is a convention to define the external variables at the beginning of a program file, as illustrated below.
#include <stdio.h>
int x = 10;
int y;
void func(void);
int main()
{
printf(“%d %d “, x, y);
x = 100, y = 200;
func ();
printf(“%d %d “, x, y);
return 0;
}
void func(void)
{
printf (“%d %d “, x, y); /* global x and y */
{
int x = 5, y = 2;
printf(“%d %d “, x , y); /*local x and y */
}
x = 50, y = 60;
}
In this program, the variables x and y are external variables initialized to 10 and 0 (default value), respectively. These variables are global and are accessible in both the functions, main and func. The main function first prints their values and assigns new values to them (100 and 200). The func function is then called which first prints these values. A block in the func function reuses these variable names to define local variables x and y with values 5 and 2, respectively, and prints them. As local variables get preference over global variables, the values are printed as 5 and 2. The func function then assigns new values (50 and 60) to variables x and y (global variables) outside the block. Finally, control returns to the main function where the values of x and y (global variables) are printed again as 50 and 60. The program output is given below.
10 0 100 200 5 2 50 60
You have probably noticed that the extern keyword was not used in the previous program. This keyword declares that a variable is external, i. e., its definition is provided in some other part of the program. By default, an external variable is accessible from the point of its definition till the end of that file. However, if we wish to access an external variable in a file before it is declared, we need to use the extern declaration, as illustrated below.
#include <stdio.h>
void func(void);
int main()
{
extern int x; /* declaration */
printf(“%d “, x);
x = 20;
func ();
printf(“%d “, x);
return 0;
}
int x = 10; /* definition */
void func(void)
{
printf(“%d “, x);
x = 30;
}
In this example, variable x is defined as an external variable with value 10 after the main function. Thus, it is not accessible in the main function and an extern declaration is required to enable this. However, x is accessible from function func as it is defined before that function. Thus, explicit declaration of variable x is not required in function func. When this program is executed, the external variable x is first initialized to 10. The main function prints its value and assigns a new value (20) to it. Then it calls function func where this value of x is printed; new value (30) is then assigned. Control then returns to the main function where this value of x is printed again. Thus, the output of the program is
10 20 30
Note that the extern declaration of a variable should match with its definition with regard to data type, type modifier and storage class; otherwise, the compiler will report an error. The extern declaration does not create variable and it can be specified any number of times, even within the same scope.
Finally note that the C language allows a program to be split into multiple program files (. c files) and header files (. h files). To access an external variable defined in another file, we need to declare that variable using an extern declaration in files where that variable is to be accessed. Consider the program given below.
main. c file: func. c file:
#include <stdio.h> #include <stdio.h>
extern int x;
int x = 10; void funcl (void)
int y = 20; {
printf(“%d “, x};
int main () x = 100;
{ }
funcl(); void func2 (void)
func2 (); {
printf(“%d %d ” ,x,y); extern int x, y;
return 0; printf(“%d %d “,x,y);
} x = 200, y = 300;
}
The main. c file defines two external variables, x and y, with initial values 10 and 20, respectively. The main function in this file calls two functions, funcl and func2, which are defined in another file, func. c and then prints the values of x and y.
The func. c file declares x as an external variable at the beginning. Thus, x in this file refers to variable x defined in the main. c file. The function func 1 prints the value of x (10) and assigns it a new value (100). The function func2 declares x and y as external variables. Thus, these variables refer to variables x and y defined in the main. c file. The function func2 then prints the values of x (100) and y (20) and then assigns them new values (200 and 300, respectively). These modified values are printed by the printf statement in the main function. The program output is given below.
10 100 20 200 300
Note that variable x is declared twice in the func. c file. An external variable can be declared any number of times. However, it can be defined only once.