Scope & Storage Classes

C · Lesson 14 of 32 8 min read Updated

Every variable in C has two properties that beginners often mix up. Its scope is the part of the code where its name can be used. Its lifetime, which the standard calls its storage duration, is how long its memory exists while the program runs.

In this lesson you will learn block scope and file scope, see how static makes a local variable remember its value between calls, and use static and extern to control which variables other files can see.

Block scope

A variable declared inside braces has block scope: it can be used from its declaration to the closing brace of that block. A function's parameters have the scope of the function body.

C
#include <stdio.h>

int main(void)
{
    int x = 10;
    printf("Outer x: %d\n", x);

    {
        int x = 20;   // a new x that hides the outer one
        int y = 5;
        printf("Inner x: %d, y: %d\n", x, y);
    }

    printf("Outer x again: %d\n", x);
    // printf("%d\n", y);   // error: 'y' undeclared
    return 0;
}
Output
Outer x: 10
Inner x: 20, y: 5
Outer x again: 10

The inner block declares its own x, which shadows (hides) the outer one until the block ends. The outer x is untouched. After the block, y no longer exists, so using it is a compile error. Shadowing is legal but confusing; GCC's -Wshadow option warns about it.

Local variables have automatic storage duration: they are created each time the block is entered and destroyed when it ends. That is why a function's local variables start fresh on every call.

File scope: global variables

A variable declared outside every function has file scope: every function below it in the file can use it. Such variables are often called global variables. They live for the whole run of the program and, unlike local variables, start at zero if you do not initialise them.

C
#include <stdio.h>

int visitors;   // global: starts at 0 automatically

void gate_entry(void)
{
    visitors++;
}

int main(void)
{
    gate_entry();
    gate_entry();
    gate_entry();
    printf("Visitors today: %d\n", visitors);
    return 0;
}
Output
Visitors today: 3

Globals are convenient but risky. Any function can change them, so when a value is wrong, you must search the whole program to find out who changed it. Prefer passing values as parameters and returning results. Keep globals for things that really belong to the whole program, and give them clear names.

static local variables

Adding static to a local variable changes its lifetime but not its scope. The variable is still visible only inside its function, but it is created once, before the program starts, and keeps its value between calls.

C
#include <stdio.h>

int next_token(void)
{
    static int token = 100;   // initialised once, remembers its value
    token++;
    return token;
}

int count_calls_wrong(void)
{
    int calls = 0;            // a fresh variable on every call
    calls++;
    return calls;
}

int main(void)
{
    for (int i = 0; i < 3; i++) {
        printf("Token %d, wrong counter %d\n", next_token(), count_calls_wrong());
    }
    return 0;
}
Output
Token 101, wrong counter 1
Token 102, wrong counter 1
Token 103, wrong counter 1

token behaves like a bank's token machine: each call hands out the next number. The line static int token = 100; runs its initialisation only once, not on every call. In count_calls_wrong, calls is automatic, so it is reset to 0 every time and the function always returns 1.

How long variables live

C has four storage durations:

Storage durationWhich variablesLifetime
AutomaticLocal variables and parametersFrom entering the block until leaving it
StaticGlobal variables and static localsThe whole run of the program
AllocatedMemory from malloc and friendsUntil you call free
ThreadVariables marked thread_localThe life of one thread

Allocated memory is the subject of Dynamic Memory. Thread storage matters only in programs that run several threads at once; C23 spells the keyword thread_local, while C11 code writes _Thread_local.

A classic bug comes from automatic lifetime: a function must never hand back the address of one of its local variables, because that memory is gone as soon as the function returns. GCC warns "function returns address of local variable". You will see why in Pointers, NULL & nullptr.

static and extern across files

Real programs are split into several .c files. At file level, two keywords decide what other files can see:

  • A global variable or function is shared with other files by default. Another file uses it after declaring it with extern, which says "this exists, but it is defined somewhere else".
  • Adding static at file level makes a global variable or function private to its own file. Other files cannot see it, even with extern.

Here is a program made of two files:

C
// counter.c
static int count = 0;       // private to this file
int total_visitors = 0;     // shared with other files

void add_visitor(void)
{
    count++;
    total_visitors++;
}

int visitors_here(void)
{
    return count;
}
C
// main.c
#include <stdio.h>

extern int total_visitors;  // defined in counter.c
void add_visitor(void);
int visitors_here(void);

int main(void)
{
    add_visitor();
    add_visitor();
    printf("Here: %d, total: %d\n", visitors_here(), total_visitors);
    return 0;
}
Terminal
gcc -std=c23 -Wall -Wextra main.c counter.c -o visitors
./visitors
Output
Here: 2, total: 2

main.c can read total_visitors directly but can reach count only through the function visitors_here. If main.c tried extern int count;, the program would fail to link. Making helpers static is good practice: it keeps each file's internals hidden and avoids name clashes between files. In real projects, the extern declarations and prototypes go into a header file, as shown in Header Files & Multi-file Programs.

DeclarationScopeLifetimeVisible to other files?
int x; inside a blockBlockAutomaticNo
static int x; inside a blockBlockStaticNo
int x; outside functionsFileStaticYes
static int x; outside functionsFileStaticNo
extern int x;Wherever it appearsRefers to a definition elsewhereNot applicable

Common mistakes

  • Using a variable outside its block. A variable declared inside an if or loop body disappears at the closing brace.
  • Accidental shadowing. Declaring int total again inside a loop creates a new variable, and the outer total never changes.
  • Expecting a local variable to remember its value. Without static, locals restart on every call.
  • Too many globals. They make bugs hard to trace. Pass values through parameters instead.
  • Defining the same global in two files. Define it once in one .c file and use extern everywhere else, or the linker reports a multiple definition.

Key points

  • Scope is where a name can be used; lifetime is how long the memory exists.
  • Local variables have block scope and automatic lifetime; they start fresh on each entry.
  • Global variables have file scope, live for the whole run and start at zero.
  • A static local is initialised once and keeps its value between calls.
  • At file level, static makes a variable or function private to its file; extern declares one defined in another file.
  • Prefer parameters and return values over global variables.

Exercise

Write a function void record_sale(int amount) for a Lucknow sweet shop. It should keep a running total of the day's sales in a static local variable and print the sale and the new total each time. Call it from main with 120, 80 and 250.

Show solution
C
#include <stdio.h>

void record_sale(int amount)
{
    static int total = 0;
    total += amount;
    printf("Sale: Rs %d, total today: Rs %d\n", amount, total);
}

int main(void)
{
    record_sale(120);
    record_sale(80);
    record_sale(250);
    return 0;
}
Output
Sale: Rs 120, total today: Rs 120
Sale: Rs 80, total today: Rs 200
Sale: Rs 250, total today: Rs 450

total is created once and keeps its value between calls, yet no other function can change it, which is safer than a global variable.

Quick Quiz

1. What is the scope of a variable declared inside an if block?

2. What does static do to a local variable?

3. What value does a global int have if you do not initialise it?

4. What does static int helper_count; written outside all functions mean?

Learn C with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call