Storage Classes in C++
Learn via video course

Overview
C++ Storage classes help define the lifetime and visibility of variables and functions within a C++ program. Lifetime refers to the duration till a variable or function remains active & visibility is the accessibility of the said variable or function relative to various sections of a program. Storage classes help determine the longevity and accessibility along with memory allocation of variables and functions. There are several storage classes; their visibilities may be global or local.
Scope of the Article
- This article explains the various storage classes in C++.
- This article gives an overview of the visibility of variables and functions and their scopes.
What are C++ Storage Classes?
Storage classes are type specifier in C++ that helps specify the lifetime and visibility of variables and functions within a C++ program. C++ storage classes help determine the existence of a particular variable or function within a program's run time. They tell us which part of a program we can access a variable. Storage classes are especially significant because they determine how any variable may be used within a program or any method, especially in the context of their scope, lifetime, and storage.
Storage classes give us the following information about a variable or function:
- The scope or the part of the program up to which the function or variable can be used.
- The location where the function or variable is stored.
- The initial value of variables.
- The lifetime of a variable.
- Accessibility of a variable.
There are several storage classes in C++. They are as follows:
- automatic
- static
- register
- external
- mutable
- thread_local
Each of these storage classes implies different visibilities, lifetimes, and scopes of functions or variables that are declared with. The visibility or scope of a variable can be broadly divided into two categories:
- Local
- Global
Syntax:
The following syntax is used to define the storage class for a variable:
Where, storage_class specifies the storage class. var_data_type specifies the variable’s data type. var_name specifies the variable name.
The following keywords are used to specify the various storage classes:
Storage_Class | Keyword |
---|---|
Automatic | auto |
Static | static |
Register | register |
External | extern |
Mutable | mutable |
Thread Local | thread_local |
Global Variables
Global Variables are declared outside all functions of a program, and their scope comprises the entire program. i.e., It can be used and modified by all parts of a program. Global variables are declared at the start of a program; likewise, their lifetime ends at the end of the program.
Global variables are also declared with the standard syntax used to declare variables:
Where, storage_class specifies the storage class. var_data_type specifies the variable’s data type. var_name specifies the variable name.
The following is an example of a global variable:
Output:
In the code snippet above, the variable c is a global variable declared outside all functions. It can be accessed by both the test() and main() functions.
Local Variable
A variable declared inside a function, i.e., inside the body of a function within braces, is called a local variable. A local variable's scope and lifetime are limited to within the function in which it was defined. In other words, a local variable exists and can be used only within the defined function.
A local variable is also called an automatic variable as it exists within a function, and its lifetime expires automatically outside the function.
Local variables are also declared with the standard syntax used to declare variables:
Where, storage_class specifies the storage class. var_data_type specifies the variable’s data type. var_name specifies the variable name.
The following code is an example of a local variable in C++:
Output:
In the program above, the i variables are both local variables and, as such, can be accessed only by the function they are declared in. Hence, calling the test() function within main() does not change the value of the i variable within main().
What are Lifetime and Visibility?
In C++, the lifetime of a variable refers to the period within which memory is allocated to a particular variable. Or, in other words, the period in which a variable exists within a program. On the other hand, the visibility of a variable in C++ refers to the extent to which a variable can be used within the parts of a program.
The lifetime of a variable is associated with the memory allocation of a variable. For example, a variable using the static storage class has a lifetime equal to the whole program. On the other hand, a variable's visibility controls how much the rest of the program may access it. For example, a variable using the static storage class has its visibility as local and, as such, can only be accessed within the function block it is declared in.
Types of Storage Classes in C++
In the following section, we look at the types of storage classes in C++.
1. Automatic
The automatic storage class in C++ is the default storage class for all local variables. The auto keyword in C++ is used to declare the automatic storage class for variables. The automatic storage class in C++ can also be used for the automatic deduction of data type and, as such, can be used while declaring a variable without specifying the data type.
Following C++11 standards, the auto keyword is now used for the automatic deduction of datatype in C++. The compiler automatically infers the data type of a variable declared auto based on the data assigned to it.
Automatic storage class in C++ is the default storage class for variables declared without a specific storage class and has a local scope and lifetime.
The lifetime of an automatic variable is the function block within which the variable is declared, and the visibility of the variable is also local. Its initial value is a garbage value.
Auto variables are declared without specifying the storage class for the variable as:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output:
In the code snippet above, we have declared x inside the test() function as a local variable, and it has been automatically set to the auto storage class. The variable y has been declared with the auto keyword, which automatically infers its type using the assigned value of the variable. Within the main() function, we have declared two more variables, x and y, similar to the other two. Due to the scopes of the variables being local, calling the test function within main() does not change the values of x and y in main().
2. Static
The static storage class in C++ is used to declare a variable that, once initialized, is not deallocated when the variable goes out of scope. The lifetime of such a variable is the entire program; as such, the variables do not lose their values within function calls. It is beneficial in recursive calls as static variables are initialized only once within a function block and can be changed on each recursive call without getting initialized over and over. Global variables may also be declared static, but such variables cannot be used outside the file in which it is declared.
Static storage class in C++ defines variables whose lifetime is equal to the program's lifetime, even if their scope is local.
The lifetime of a static variable is the entire program, but a static variable's visibility is local. Its initial value is zero.
Static variables are declared with the following syntax:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output:
In the code snippet above, we have declared a static variable, a, within the test() function. Within the main() function, we have again declared a static variable, a. When the test() function is called within main() twice, the local variable within test() is initialized only once, and it retains its value within function calls. The a variable declared within main() is not changed with calls to test() as it has a local scope.
3. Register
The register storage class in C++ utilizes CPU registers to store data to access data quickly in a function. The functionality of register variables is similar to automatic variables. The only difference is that the compiler tries to store the variable in the register instead of the memory if the registers are free. The compiler stores the register variables in the memory if no registers are free.
It is important to note that the address of a register variable cannot be accessed as they are stored in registers and not the memory. Register storage class in C++ is used to declare variables stored in registers by the compiler for faster access.
Register variables are used when a program requires frequently accessing a variable. Registers, being a part of the processor, allow quick access to memory, much faster than standard memory. Register variables have local visibility, and their lifetime is the function block within which they are declared. Register variables are initialized with a garbage value.
Syntax: Register variables are declared with the following syntax:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output:
In the code snippet above, we have declared a register variable in both test() and main(), initialized separately as the register has a local scope.
4. External
The external storage class in C++ is used when a variable or function is declared on a separate function block from the definition. That is, the variable or function may be defined in one code block or even file and then be declared and used in another code block or file. Using the extern keyword tells the compiler that the variable or function is defined elsewhere. It specifies external linkage for a variable. In the case of variable declarations without definition using the external storage class, no memory is allocated to the variable until it is defined elsewhere.
The external storage class in C++ is used to declare variables defined elsewhere and have their lifetime and visibility as global.
The external storage class is specified using the extern keyword. Its lifetime is the whole program, and its visibility is global, i.e., it is accessible by any block within the program. It is initialized with a value of 0. The external storage class is usually used when a variable or function is declared in separate files within a large program.
Syntax: External variables are declared with the following syntax:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output
In the code snippet above, we have declared the variable x as a global variable. The extern keyword within test() specifies that the variable x is declared somewhere else outside the current code block and, as such, allows us to access the global variable.
5. Thread_local
The Thread-local storage class in C++ defines variables local to each thread created in the program. In the case of a program with multiple threads, each thread receives its instance of variables declared as thread-local. Thread_local variables may seem like static or global variables, but their lifetime is only the duration of a particular thread. That is, thread-local variables are created on thread creation and are disposed of when the thread is exited.
Thread Local storage class in C++ is used to declare variables local in scope and lifetime to the thread in which they are declared.
The Thread local storage class is specified using the thread_local keyword. Its lifetime is the same as the lifetime of a thread, and its visibility is local.
Syntax: Thread local variables are declared with the following syntax:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output
In the code snippet above, we have declared a thread-local variable with an initial value of zero. We have changed the thread-local val value inside the test variable and printed it and its address. Inside the main, we have changed the value of val and created a thread to run the test() function. We have also printed the variable and its address before and after the thread. We observe that a separate instance of the val is created in each thread with different initialization and address.
6. Mutable
The mutable storage class in C++ is used where any particular data member of a struct or a class must be modified in an otherwise const object. Any constant object of a class or structure will always have constant data members and functions. But using the mutable storage class allows us to change those variables and use them as non-constant. It is used when one wishes to change only a few data members in a class or structure without changing the value of the others.
Mutable storage class in C++ is used to declare variables to be changed in an otherwise constant object or class.
The mutable storage class has a lifetime equal to that of the class, and it has local visibility. The initial value of a mutable storage class is a garbage value.
Syntax: Mutable variables are declared with the following syntax:
Where, var_data_type specifies the variable’s data type. var_name specifies the variable name.
For example:
Output
In the code snippet above, we have declared a class Test with several data members and functions. Within main(), we have declared constant objects of class Test. Thus all data members of Test are also constant. However, we have declared x as mutable; hence, it can be modified.
Features of Storage Classes in C++
Storage Class | Keyword | Lifetime | Visibility | Initial Value |
---|---|---|---|---|
Automatic | auto | Function Block | Local | Garbage |
External | extern | Whole Program | Global | Zero |
Static | static | Whole Program | Local | Zero |
Register | register | Function Block | Local | Garbage |
Mutable | mutable | Class | Local | Garbage |
Thread-Local | thread_local | Thread | Local |
Storage Duration
According to C++ Language Standard, Storage Duration is “the property of an object that defines the minimum potential lifetime of the storage containing the object.” In other words, we can refer to storage duration as the minimum amount of time within which a variable might be usable or accessible. There are four storage durations in C++.
They are as follows:
1. Static Duration
Global variables declared with the static keyword are initialized during program initialization and stay in memory until the end of the program. Local static variables are initialized when the program enters its containing block and stays in memory until the end of the program. There also exists only one instance of the variable during the program.
2. Automatic Duration
Automatic duration is the default storage duration of variables if they are declared without specifying the storage duration. Variables with automatic duration are initialized at the time of their declaration. They are destroyed when the program leaves the code block containing the declaration or a new value is assigned to the variable. It is important to note that one could use the auto keyword to declare the automatic duration of a variable explicitly. Still, as of C++11, the auto keyword is also used to deduce the data type of a variable automatically and, as such, cannot be used when declaring a variable with a specified data type.
3. Dynamic Duration
Variables using dynamic storage duration may be dynamically allocated and deallocated memory using dynamic memory allocation functions. Dynamic duration is usually used while declaring dynamic arrays and the like.
4. Thread Duration
Variables with thread storage duration are initialized at a thread's start and destroyed when the thread is exited. Each thread may have a separate instance of such a variable in a program. Only variables declared with thread_local have this thread duration.
Linkage
Linkage in C++ is the property. The linker links identifiers to variables. In other words, the linkage of a storage class determines whether a variable can be declared and whether the identifier can refer to one or multiple objects within a program. Say we have a variable with a particular identifier defined within a particular scope. The linkage of the variable would determine if we can make a separate definition of the variable identifier within that scope. If the linkage does not allow for redeclaration of the variable, it will throw a 'Redeclaration error'. A function identifier may be declared repeatedly within a particular C++ code. However, it can only be defined once. This is the "One Definition Rule" (ODR).
For any variable declared in a separate scope, in case there is a linkage, it means that any reference to the identifier is a reference to the same variable in all scopes. On the other hand, if a variable, function, or other entity is declared in multiple scopes without sufficient linkage, then several entity instances are generated.
C++ has the following types of linkages:
1. No Linkage
In case of no linkage, the identifiers can be referred to only within the scope they are declared in. In any other cases, separate initialization for each declaration is done. Any of the following names declared at block scope have no linkage:
- Variables that aren't explicitly declared extern (regardless of the static modifier);
- Local classes and their member functions;
- Other names are declared at block scopes such as typedefs, enumerations, and enumerators.
- Names not specified with an external module (since C++20) or internal linkage also have no linkage, regardless of which scope they are declared in.
2. Internal Linkage
In the case of internal linkage in C++, identifiers can be referred to from all scopes in the current translation unit. A translation unit in C++ refers to a file containing source code, header files, and other dependencies grouped to form one executable block. Any of the following names declared at namespace scope have internal linkage:
- Variables, variable templates (since C++14), functions, or function templates declared static;
- Non-volatile non-template (since C++14), non-inline (since C++17), non-exported (since C++20) const-qualified variables (including constexpr) that aren't declared extern and aren't previously declared to have external linkage;
- Data members of anonymous unions.
- All names declared in an unnamed namespace or a namespace within an unnamed namespace, even ones explicitly declared extern, have internal linkage. (since C++11)
3. External Linkage
With external linkage in C++, the identifiers can also be referred to from the scopes in the other translation units. Variables and functions with external linkage also have language linkage, which makes it possible to link translation units written in different programming languages.
Any of the following names declared at namespace scope have external linkage unless they are declared in an unnamed namespace or their declarations are attached to a named module and are not exported (since C++20):
- Variables and functions not listed above (that is, functions not declared static, non-const variables not declared static, and any variables declared extern);
- Enumerations;
- Names of classes, their member functions, static data members (const or not), nested classes and enumerations, and functions first introduced with friend declarations inside class bodies;
- Names of all templates not listed above (not function templates declared static).
4. Module Linkage
The identifier can be referred to only from the scopes in the same module unit or the other translation units of the same-named module. Identifiers with module linkage can be used only within their translation units and not within other translation units. They are like global variables within modules.
Names declared at namespace scope have module linkage if their declarations are attached to a named module, are not exported, and don't have internal linkage.
Static local variables
Static local variables belong to the static storage class in C++ but have a local scope, like the function in which it was called. A static local variable is initialized only once, irrespective of the function call number, and will retain its value after each function call. Static local variables are local in scope and, as such, cannot be used outside the code block in which it is declared. However, static local variables have a lifetime equal to that of the program and thus retain their values after function calls.
For example:
Output:
In the code snippet above, the variable n inside test() is a static variable and is initialized only once. On each call to test() from main(), the n variable retains its value. The scope of n is local and, as such, cannot be referred to from main().
Translation-unit-local Entities
Translation-unit-local (TU-local) entities are introduced to prevent entities that are supposed to be local (not used in any other translation unit) from being exposed and used in other translation units.
An entity is TU-local if it is:
- a type, function, variable, or template that has a name with internal linkage,
- does not have a name with linkage and is declared, or introduced by a lambda expression, within the definition of a TU-local entity,
- a type with no name that is defined outside a class-specifier, function body, or initializer or is introduced by a defining-type-specifier (type-specifier, class-specifier, or enum-specifier) that is used to declare only TU-local entities,
- a specialization of a TU-local template,
- a specialization of a template with any TU-local template argument,
Example: Automatic vs. Static Initialization
Output:
In the code snippet above, within the test() function, we have declared two variables, one static and one automatic. The test() function is called ten times in main(). As we can see from the output, on each function call, the static variable retains its value until the end of the program. On the other hand, the auto variable is initialized to zero on each call and prints the same output each time.
Conclusion
- C++ storage classes help define the lifetime and visibility of variables and functions within a C++ program.
- The scope of C++ variables or functions can be either local or global.
- There are several C++ storage classes, namely Automatic, Register, Static, External, thread_local, and Mutable.
- Storage Duration in C++ refers to the minimum time a variable exists within a program and might be usable or accessible.
- Linkage in C++ is the property that determines if a variable declared separately in multiple scopes refers to the same instance of the variable or separate.