Scope & Storage Classes
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.
#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;
}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.
#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;
}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.
#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;
}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 duration | Which variables | Lifetime |
|---|---|---|
| Automatic | Local variables and parameters | From entering the block until leaving it |
| Static | Global variables and static locals | The whole run of the program |
| Allocated | Memory from malloc and friends | Until you call free |
| Thread | Variables marked thread_local | The 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
staticat file level makes a global variable or function private to its own file. Other files cannot see it, even withextern.
Here is a program made of two files:
// 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;
}// 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;
}gcc -std=c23 -Wall -Wextra main.c counter.c -o visitors
./visitorsHere: 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.
| Declaration | Scope | Lifetime | Visible to other files? |
|---|---|---|---|
int x; inside a block | Block | Automatic | No |
static int x; inside a block | Block | Static | No |
int x; outside functions | File | Static | Yes |
static int x; outside functions | File | Static | No |
extern int x; | Wherever it appears | Refers to a definition elsewhere | Not applicable |
Common mistakes
- Using a variable outside its block. A variable declared inside an
ifor loop body disappears at the closing brace. - Accidental shadowing. Declaring
int totalagain inside a loop creates a new variable, and the outertotalnever 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
.cfile and useexterneverywhere 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
staticlocal is initialised once and keeps its value between calls. - At file level,
staticmakes a variable or function private to its file;externdeclares 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
#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;
}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?
A variable declared inside braces exists only until the closing brace of that block.
2. What does static do to a local variable?
A static local has static lifetime: it is initialised once and keeps its value.
3. What value does a global int have if you do not initialise it?
Variables with static storage duration, including globals, start at zero.
4. What does static int helper_count; written outside all functions mean?
At file level, static gives internal linkage: only code in the same file can use the name.
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
