Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Understanding C Storage Classes: Scope & Lifetime

Recommended resources

Hello! Welcome to your third lesson.

In our last session, we explored the function call stack in detail, focusing on how stack frames are created for local variables and how program flow is managed. This provides a perfect foundation for today's topic.

Today, we will focus on storage classes. The goal is to distinguish between auto, static, extern, and register storage classes, predicting variable lifetime and scope in code snippets. Mastering these is crucial, as interviewers frequently use questions about them to test your understanding of a variable's fundamental properties beyond just its data type.

1. The Four Properties of a Variable

Every variable in C is defined by more than just its type (like int or char*). Its storage class specifier dictates four crucial attributes that determine its behavior throughout the program's execution.

Storage Classes in C

To begin, let's formally define these attributes. This article from Scaler provides a concise introduction to what storage classes control.

Please read the introduction and the first three subsections titled 'Scope', 'Default definition value', and 'Lifetime'. These will establish the core concepts we'll be discussing.

To summarize, the four key properties are:

  1. Scope: Where in the code is the variable's name visible and accessible? (e.g., within a single function, or across the entire program).
  2. Lifetime: How long does the variable exist in memory? (e.g., only during a function call, or for the entire program run).
  3. Storage Location: Where is the memory for the variable allocated? (e.g., stack, data segment, CPU register).
  4. Default Initial Value: What value does the variable hold if you don't explicitly initialize it? (e.g., zero, or a random "garbage" value).

The four storage class keywords—auto, register, static, and extern—are your tools for controlling these properties. Here is a high-level summary.

Storage Classes in C Summary Table
This table provides a summary of the four C storage classes, detailing their storage location, default initial value, scope, and lifetime. It's a useful reference to keep in mind as we explore each one.

2. auto and register: The Local Workers

These two storage classes apply to variables defined inside a block (like a function).

auto

The auto storage class is the default for all local variables. Since it's the default, you almost never see it written explicitly.

void my_function() {
    int local_var;       // This is an auto variable by default
    auto int another_var; // This is explicitly an auto variable
}

As we saw in the last lesson, auto variables are directly tied to the function call stack:

  • Storage: Allocated on the stack when the function is called.
  • Lifetime: Exists only until the function returns. The memory is then freed.
  • Scope: Local to the block where it is defined.
  • Default Value: Garbage (whatever was left in that memory location).

register

The register keyword is a hint to the compiler to store the variable in a CPU register instead of RAM. Accessing a CPU register is much faster than accessing RAM, so this was historically used for frequently accessed variables like loop counters.

void fast_loop() {
    register int i;
    for (i = 0; i < 1000000; i++) {
        // ...
    }
}

However, modern compilers are extremely good at optimization and will often make better decisions than a human about which variables to place in registers. Therefore, register is more of a legacy feature.

The most important takeaway for interviews is understanding its limitation: you cannot take the address of a register variable. This is logical: CPU registers don't have memory addresses in the same way that RAM does.

C_147 Storage Classes | Gate Question on Storage Classes in C

This video from Jenny's Lectures CS IT demonstrates exactly what happens when you try to get the address of a register variable.

Watch the segment from 08:52 to 10:37. Notice how the code fails to compile and the reason for the error.


3. static: The Persistent Local

The static keyword dramatically changes a local variable's behavior, making it a common subject of tricky interview questions. When applied to a local variable, static gives it static lifetime but retains its local scope.

  • Lifetime: The variable persists for the entire duration of the program. It is not destroyed when the function returns.
  • Storage: It's stored in the data or BSS segment, not the stack.
  • Default Value: Initialized to zero by default.
  • Initialization: It is initialized only once, when the program starts. On subsequent function calls, the initialization statement is ignored.

Consider this classic example:




#include <stdio.h>

void count_calls() {
    static int static_counter = 0; // Initialized only once
    int auto_counter = 0;          // Initialized on every call

    static_counter++;
    auto_counter++;

    printf("Static: %d, Auto: %d\n", static_counter, auto_counter);
}

int main() {
    count_calls(); // Prints "Static: 1, Auto: 1"
    count_calls(); // Prints "Static: 2, Auto: 1"
    count_calls(); // Prints "Static: 3, Auto: 1"
    return 0;
}

The static_counter remembers its value between calls, while auto_counter is reset every time.

Now, let's analyze a more complex scenario that tests this concept thoroughly.

C_145 Solved Problems on Storage Classes in C | Technical Interview Question

This video from Jenny's Lectures CS IT presents an excellent problem involving a static variable inside a function that's called from multiple parts of a for loop. This is a great way to solidify your understanding of its persistence and single-initialization rule.

Watch the detailed walkthrough from 01:54 to 08:04. Follow the trace of the num variable closely to see how it retains its value across all the function calls.


4. extern: The Global Linker

The extern keyword is used to declare a variable that is defined elsewhere. It's a promise to the compiler that a variable with a given name and type exists, and that the linker will find it later. This is essential for sharing global variables across multiple source files.

Declaration vs. Definition is a key concept here:

  • int global_var; (outside a function) is a definition. It allocates storage.
  • extern int global_var; is a declaration. It does not allocate storage; it just refers to an existing definition.

A common interview question involves what happens if an extern variable is declared but never defined. The compiler will trust the extern promise and compile the code, but the linker will fail when it cannot find the actual variable to link to, resulting in an "undefined reference" error.

C_147 Storage Classes | Gate Question on Storage Classes in C

This video provides a clear explanation of extern and demonstrates the linker error that occurs when a definition is missing.

Watch from 04:37 to 08:52. Pay close attention to the distinction between a compiler error and a linker error, and why the code examples fail at the linking stage.

By default, global variables (those defined outside any function) have external linkage, meaning they are visible to other files that use extern to declare them.


Conclusion

We've covered the four main storage classes in C, which are fundamental to controlling how and where your program stores data. Correctly using them is vital in embedded systems for managing memory, controlling access to hardware, and structuring large projects.

Key Takeaways:

  • auto: The default for local variables. They live on the stack and exist only for the duration of a function call. Their initial value is garbage.
  • register: A hint to store a local variable in a CPU register for speed. It cannot have its address taken.
  • static: When used locally, it creates a variable that lasts for the entire program, retains its value between function calls, and is initialized to zero only once.
  • extern: Used to declare a global variable that is defined in another location (e.g., another .c file). It is a contract that is fulfilled by the linker.

In our next lesson, we will dive deeper into the static keyword. We've seen how it works for local variables, but its meaning changes when applied to global variables and functions—a subtlety that is a prime source of interview questions.