.
Scope and Lifetime of Variables in C
Scope and storage class are two crucial characteristics of all variables and functions in C programming. The variable’s scope, linkage, and storage duration also referred to as its lifetime are all determined by the storage class.
Let’s dissect these ideas:
Scope
Range The term scope describes the area of a program where an identifier (such as a variable name) is “known” or can be referenced. The scope of a variable is greatly impacted by where it is declared. Several sorts of scope:
File Scope: An identifier with file scope is one that is declared outside of any function. From the moment of its definition until the end of the file, such an identifier is available in every function. File scope applies to global variables, function declarations, and function prototypes that are positioned outside of a function.
Block Scope: A block is a chunk of code that is surrounded by curly braces ({}) and has identifiers specified inside it. The block’s final right brace (}) marks the end of these identifiers’ scope. Block scope applies to local variables declared inside a function body or any block, including function parameters. An identifier in an inner block may share the same name as an identifier in an outer block if the blocks are nested, and the identifier in the inner block will conceal the outer one within its scope.
Function-Prototype Scope: Function-prototype scope refers to identifiers that are utilised exclusively in a function prototype’s argument list. In a prototype, only the types are needed; the compiler usually ignores these names.
Lifetime (Storage Duration)
Lifetime (Duration of Storage) The amount of time an identifier is stored in memory is known as its lifetime or storage duration. A variable’s storage duration is determined by its storage type. Auto, register, extern, and static are the four storage classes offered by C. According to lifespan, these are typically divided into two major categories:
- Automatic Storage Duration: When program control moves into the block where variables are defined, variables with an automatic storage duration are formed. When program control leaves the block, they are eliminated. They are present while the block is active. When the function or block completes its execution, its values are lost. When local variables are declared inside function bodies or blocks, this is the default storage duration. Because it is the default, the auto keyword is rarely used, even though it expressly indicates automatic storage duration. Although register is regarded as outdated, variables with this storage class also have automated storage duration and block scope. Unless specifically assigned, automatic variables contain “garbage” values because they are not initialised by default.
- Static Storage Duration: Static storage duration variables are allocated and initialised just once, prior to the application starting up. They maintain their values throughout the duration of the program’s operation. If no value is specified explicitly, they are initialised to zero by default. Identifiers having static storage duration are declared via the storage classes extern and static.
- e xtern: For global variables and function names defined outside of any function, this is the default storage class. Declared outside of function definitions are global variables. Their values are retained until the program ends, and they usually have file scope and can be accessed throughout the entire program.
- static: It grants the local variable block scope but static storage duration when applied to a local variable inside a function or block. This indicates that, in contrast to standard automatic local variables, the variable maintains its value between function calls or re-entry into the block. Even though a global variable or function declared at the file scope still has a static storage duration and file scope, applying static restricts its linkage (visibility) to the current file.
Example of Illustrative Code
The code that follows illustrates the various scopes and lifetimes of global, automatic local, and static local variables according to their storage class and declaration location:
// fig05_08.c
// Scoping.
#include <stdio.h>
void useLocal(void); // function prototype
void useStaticLocal(void); // function prototype
void useGlobal(void); // function prototype
int x = 1; // global variable
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);
}
// 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);
}
// 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) {
static int x = 50; // initialized once
printf("\nlocal static x is %d on entering useStaticLocal\n", x);
++x;
printf("local static x is %d on exiting useStaticLocal\n", x);
}
// 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);
}
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 useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 50 on entering useStaticLocal
local static x is 51 on exiting useStaticLocal
global x is 1 on entering useGlobal
global x is 10 on exiting useGlobal
local x in useLocal is 25 after entering useLocal
local x in useLocal is 26 before exiting useLocal
local static x is 51 on entering useStaticLocal
local static x is 52 on exiting useStaticLocal
global x is 10 on entering useGlobal
global x is 100 on exiting useGlobal
local x in main is 5
A description of the example
- Outside of any functions, a global variable called x is declared and set to 1. This x has a static storage duration and a file scope. It is accessible by any function (such as useGlobal) that isn’t obscured by a local variable of the same name and exists for the duration of the program’s execution.
- A local variable called x is declared inside main and set to 5. This x has automated storage time and block scope inside the main block’s outer block. It conceals the global x within its scope and is only present when main is operating.
- Another local variable x, initialised to 7, is introduced within main by a nested block {}. This x has automated storage time and block scope restricted to this inner block. It conceals the local x and global x of the outer main and only appears when the program is executing statements inside this inner block. This x is erased when the program leaves this inner block, and the local x of the outside main is once more visible.
- A local variable x with an initial value of 25 is declared by the useLocal function. This x has automated storage duration and block scope under useLocal. This x is created and reinitialised to 25 each time useLocal is called. When useLocal is terminated, its value is gone.
- A static local variable x with an initial value of 50 is declared via the useStaticLocal function. Because of the static keyword, this x has static storage duration even though it has block scope within useStaticLocal. The software simply initialises it once before it launches. Between useStaticLocal calls, it maintains its value.
- Since useGlobal does not declare a local variable called x to conceal the global variable, the useGlobal function accesses the global variable x. Since x is the global variable, changes made to it in useGlobal remain in effect.
You can easily observe the differences in scope and lifetime depending on where and how variables are declared by looking at the program’s output and tracking which x is accessed and how its value changes (or doesn’t change) throughout various program sections and function calls.