Storage classes in C and C++ define the scope, visibility, lifetime, and memory location of variables or functions. They determine how memory is allocated and how long variables retain their values. There are several storage classes, each serving different purposes based on variable usage and program structure.
The major storage classes in C/C++ are:
auto
register
static
extern
Important Terms
1️⃣ Scope:
Scope
refers to the visibility and lifetime of variables, functions, and other identifiers in your code. It defines where a variable can be accessed and how long it exists in memory. There are four primary types of scope in C/C++:
- Block Scope (Local Scope)
- File Scope (Global Scope)
- Function Scope
- Class/Namespace Scope (C++ specific
Block Scope (Local Scope)
- Definition: Variables declared inside a block
{}
have block scope. These variables are only visible and accessible within the block where they are defined. The block is typically a function, a loop, or a conditional statement. - Lifetime: These variables exist only while the block is executing and are destroyed when the block finishes.
void myFunction() {
int x = 10; // x has block scope (local to myFunction)
if (x > 5) {
int y = 20; // y has block scope (local to this if block)
// y is accessible here
}
// y is not accessible here
}
Key Points:
- Variables in block scope are destroyed when the block ends.
- Block-scoped variables are local to the block and cannot be accessed outside of it.
2 File Scope (Global Scope)
- Definition: Variables and functions declared outside of all functions or blocks have file scope, meaning they are visible throughout the entire file in which they are defined. These variables are commonly referred to as global variables.
- Lifetime: They exist for the entire duration of the program.
int globalVar = 100; // globalVar has file scope (visible throughout this file)
void myFunction() {
globalVar = 200; // Accessible and modifiable here
}
void anotherFunction() {
globalVar = 300; // Accessible and modifiable here too
}
Key Points:
- File-scoped variables are accessible from any function within the file.
- Global variables persist for the entire duration of the program and can lead to side effects if not handled carefully.
3 Function Scope
- Definition: Labels (used for
goto
statements) have function scope. These labels are visible throughout the function in which they are declared, but only used forgoto
jumps within the same function. - Lifetime: Labels exist throughout the function’s execution, but they do not occupy memory like variables.
void myFunction() {
int x = 10;
if (x > 5) {
goto label; // Jump to label
}
label: // Label with function scope
printf("Reached the label");
}
- Key Points:
- Labels can be used throughout the function but cannot cross function boundaries.
goto
statements and labels are generally discouraged in modern programming because they make code harder to read and maintain.
4 Class/Namespace Scope (C++ specific)
- Class Scope: In C++, members of a class (variables, functions) have class scope. They are accessible within the class and its methods but are often private to the class unless specified otherwise.
- Class members are either accessed via an object of the class or through the class itself (in case of static members).
class MyClass {
public:
int publicVar; // public member (accessible anywhere through an object)
private:
int privateVar; // private member (accessible only inside the class)
void display() {
privateVar = 10; // Accessible here
}
};
int main() {
MyClass obj;
obj.publicVar = 20; // Accessible
// obj.privateVar = 30; // Error: privateVar is not accessible here
}
- Namespace Scope: Variables, functions, and classes declared inside a namespace have namespace scope. These are accessible within the namespace and can be referenced outside using the namespace name or
using
directive.
namespace MyNamespace {
int var = 10;
}
int main() {
MyNamespace::var = 20; // Access using the scope resolution operator
return 0;
}
2️⃣ Visibility
Visibility refers to the accessibility of variables, functions, and other identifiers from different parts of a program. It is closely related to scope, but while scope defines where a variable exists in a program, visibility defines where a variable can be accessed from within its scope.
Levels of Visibility in C/C++:
- Block (Local) Visibility
- File (Global) Visibility
- External Visibility
- Class Visibility (C++ specific)
1 Block (Local) Visibility:
- Definition: Variables defined within a block (such as within a function or inside loops) are only visible inside that block. They cannot be accessed from outside the block, even if the block is inside a function that has a broader scope.
void myFunction() {
int x = 10; // x is visible only inside this block
if (x > 5) {
int y = 20; // y is visible only inside this block
}
// y is not visible here
}
- Key Points:
- Variables inside a block have local (block-level) visibility and are not accessible outside of the block in which they are defined.
- This limits the visibility to ensure the variable is only used within the intended block, avoiding unintended access from outside.
2 File (Global) Visibility
- Definition: Variables and functions declared outside of all functions (i.e., at the top level in a file) have file-level visibility. They can be accessed by any code in the same file. These are known as global variables or global functions.
int globalVar = 100; // globalVar is visible throughout this file
void myFunction() {
globalVar = 200; // globalVar is accessible here
}
- Key Points:
- File-scoped variables and functions are visible to all functions within the file.
- These variables have global visibility within the file unless modified with the
static
keyword.
3 External Visibility
- Definition: By default, global variables and functions have external visibility, meaning they can be accessed by other files in the same program if declared with the
extern
keyword. - External Linkage: Using
extern
makes a variable or function visible across multiple files, allowing them to share the same variable or function.
// File 1
int sharedVar = 50; // visible globally
void sharedFunction() {
// Function definition
}
// File 2
extern int sharedVar; // sharedVar is accessed from another file
void anotherFunction() {
sharedVar = 100; // sharedVar is accessible here
}
- Key Points:
- External visibility allows variables and functions to be shared across multiple files.
- Use
extern
to declare that a variable or function is defined in another file, and to make it visible in the current file.
4 Class Visibility (C++ Specific)
- Definition: In C++, visibility of class members (variables and functions) is controlled using access specifiers:
public
,private
, andprotected
. These specifiers determine whether the class members are visible from outside the class, within derived classes, or only inside the class itself.
Access Specifiers:
- public: Members are accessible from anywhere.
- private: Members are only accessible within the class.
- protected: Members are accessible within the class and derived classes.
class MyClass {
public:
int publicVar; // public visibility: accessible everywhere
private:
int privateVar; // private visibility: accessible only within MyClass
protected:
int protectedVar; // protected visibility: accessible in MyClass and derived classes
};
int main() {
MyClass obj;
obj.publicVar = 10; // Accessible here
// obj.privateVar = 20; // Error: privateVar is not accessible
return 0;
}
- Key Points:
- public members are visible everywhere, private members are only visible inside the class, and protected members are visible within the class and derived classes.
3️⃣ Lifetime
Lifetime refers to the duration for which a variable exists in memory during the execution of a program. It determines how long a variable retains its value and when it is created and destroyed.
Types of Lifetime
- Automatic Lifetime
- Static Lifetime
- Dynamic Lifetime
- Thread Lifetime (C++11 and later)
1 Automatic Lifetime
- Definition: Variables with automatic lifetime are automatically created and destroyed when a block of code (such as a function or a loop) is executed. They are typically local variables declared within functions or blocks.
- Creation: Variables with automatic lifetime are created when the block in which they are declared is entered.
- Destruction: They are destroyed when the block is exited
void myFunction() {
int x = 10; // x has automatic lifetime
// x is created when the function is called
// x is destroyed when the function exits
}
- Key Points:
- Automatic variables are stored on the stack.
- Their lifetime is limited to the duration of the block or function in which they are declared.
2 Static Lifetime
- Definition: Variables with static lifetime are created when the program starts and destroyed when the program ends. They retain their value between function calls or across different invocations.
- Creation: Static variables are initialized once and retain their value throughout the program’s execution.
- Destruction: They are destroyed when the program exits.
void myFunction() {
static int count = 0; // count has static lifetime
count++;
std::cout << count << std::endl; // Count retains its value between calls
}
int main() {
myFunction(); // Output: 1
myFunction(); // Output: 2
return 0;
}
- Key Points:
- Static variables are stored in the data segment of memory.
- They can be either global or local, with local static variables retaining their value across multiple function calls.
3 Dynamic Lifetime
- Definition: Variables with dynamic lifetime are allocated and deallocated manually using dynamic memory allocation functions such as
malloc
/free
(in C) ornew
/delete
(in C++). Their lifetime is controlled by the programmer. - Creation: They are created when memory is allocated dynamically.
- Destruction: They are destroyed when memory is explicitly deallocated.
void myFunction() {
int* ptr = new int; // ptr has dynamic lifetime
*ptr = 10;
std::cout << *ptr << std::endl; // Output: 10
delete ptr; // Deallocate memory
}
int main() {
myFunction();
return 0;
}
- Key Points:
- Dynamic variables are managed manually using memory allocation and deallocation functions.
- Failure to deallocate memory properly can lead to memory leaks.
Major Storage Classes
1️⃣ auto
(Automatic) Storage Class
- Purpose: Specifies automatic storage duration, meaning the variable is automatically created and destroyed when the block in which it's defined is entered and exited. Variables with
auto
storage class are local to the block they are defined in. - Default: Local variables inside functions are
auto
by default. Since C++11,auto
has a new meaning related to type inference, where the compiler automatically deduces the type of the variable. - Lifetime: The variable exists during the function or block execution.
- Scope: Auto variables
Example:
void function() {
auto int num = 10; // auto is the default for local variables
// num is destroyed after the function scope ends
}
- C++11 type inference:
auto x = 5; // Compiler deduces that x is of type int
2️⃣ register
Storage Class
- Purpose: Requests the compiler to store the variable in a CPU register instead of RAM to optimize access speed. It’s used for variables that are accessed frequently, such as loop counters. However, modern compilers typically handle this automatically, so explicit use of
register
is rare. - Scope: Same as
auto
(local to the function/block). - Limitation: You cannot take the address of a
register
variable because it may not be stored in memory.
Why register
is Less Relevant Today:
- Modern Compilers: Modern compilers automatically optimize the placement of variables, including deciding whether to store variables in registers. As a result, explicitly using
register
rarely leads to any performance gains. - Deprecated in C++17: In C++17, the
register
keyword has been deprecated, and in C++20, it is completely removed. This means that in newer C++ standards, you should not useregister
.- You can use
register
in C and in C++ code up toC++14
.
- You can use
- Modern Alternative:
- Instead of relying on
register
, trust modern compilers to optimize your code efficiently. If you want to optimize performance, focus on algorithmic improvements, better use of memory, and leveraging compiler-specific optimizations.
- Instead of relying on
Example:
void fastFunction() {
register int i; // Suggests to store i in a CPU register for faster access
for (i = 0; i < 1000; i++) {
// loop
}
}
3️⃣ static
Storage Class
- Purpose: Alters the lifetime and scope of a variable:
- For local variables: A
static
variable inside a function retains its value between function calls. It is initialized only once. - For global variables and functions: Limits their visibility to the current translation unit (file), preventing them from being accessed by other files.
- For local variables: A
- Lifetime: Exists for the entire program duration, but the scope can vary (local or global).
Example (Local static
):
void counter() {
static int count = 0; // Retains value across function calls
count++;
printf("%d\n", count);
}
4️⃣ extern
(External) Storage Class
- Purpose: The
extern
keyword declares a variable or function that is defined in another file. It tells the compiler that the variable exists elsewhere, and it should not allocate storage for it. This is useful for sharing global variables or functions across different files. - Scope: Global, but visible across multiple files.
- Declaration: The variable is defined in one file and declared with
extern
in others.
Example:
File 1:
int sharedVar = 10; // Definition
File 2:
extern int sharedVar; // Declaration
void printSharedVar() {
printf("%d", sharedVar);
}
5️⃣ mutable
Storage Class (C++ only)
- Purpose: Allows a class member to be modified even if the containing object is const. Typically, when an object is
const
, none of its members can be modified. However,mutable
provides an exception for certain members. - Scope: Only applicable to non-static class members.
Example:
class MyClass {
mutable int counter;
public:
void increment() const {
counter++; // Can modify counter even in const object
}
};