Scope, Lifetime & Storage Classes
Who sees a variable, and for how longEvery variable has a scope (the part of the program that can see it) and a lifetime (how long it exists). In the lab below you see global memory next to the stack, watch an inner block hide a variable with the same name, and meet static — a local variable that never forgets.
- Scopewhere a name can be used
- Lifetimehow long the variable exists
- staticremembers its value between calls
Ready
Keyboard: ← → step · Space play / pause · R reset · Esc exit classroom mode
The ideas behind the lab
Exactly what you watched happen, in plain steps.
Two questions about every variable
Scope answers "where in the code can I use this name?" — from its declaration to the end of its block, or the whole file for a global. Lifetime answers "how long does the variable exist?" — an ordinary local lives while its block or call runs (on the stack); a global or static variable lives for the whole run (in global memory). The storage class keywords auto, static, extern and register change these rules.
int g; // global: whole file, whole run, starts at 0
void f(void)
{
int a = 1; // local (auto): only in f, only during the call
static int s = 0; // static local: only in f, but for the whole run
{
int b = 2; // block scope: only inside these { }
}
}
| Keyword | Where | Scope (visible) | Lifetime | Starts as |
|---|---|---|---|---|
| auto (default) | inside a function / block | its block | while the block runs | garbage |
| register | inside a function | its block | while the block runs | garbage (and no &) |
| static (local) | inside a function | its block | the whole run | 0, set once |
| (none) global | outside all functions | the rest of the file (+ other files with extern) | the whole run | 0 |
| static (global) | outside all functions | this file only | the whole run | 0 |
| extern | anywhere | declares a global defined elsewhere | — | — |
Local and global
- A local variable is declared inside a function or block.
- It can be used only there, and is created again on every call.
- An uninitialised local holds garbage — always give it a value.
- A global variable is declared outside all functions and starts at 0.
- Every function below it can read and change it — convenient but risky.
Blocks and shadowing
- Every { } is a block; a variable declared in it lives until the matching }.
- for (int i = 0; …) — this i exists only inside the loop.
- An inner variable with the same name hides (shadows) the outer one.
- C always uses the nearest declaration of a name.
- Shadowing is legal but confusing: choose different names.
static, extern and register
- static int n = 0; inside a function: set up once, keeps its value between calls.
- Use it for counters and "has this run before?" flags.
- static in front of a global or a function: visible only in that .c file.
- extern int total; says "total is defined in another file — use that one".
- register asks for a CPU register; compilers ignore it today, and you cannot take its address.
A global counter, a static id and loop-only variables
Every line, explained.
#include <stdio.h>
int calls = 0;
Global: every function below can use it.
int nextId(void)
{
static int id = 100;
Made once, before main. NOT reset on each call.
calls++;
Changes the global.
return ++id;
101, then 102, then 103.
}
int main(void)
{
for (int i = 0; i < 3; i++) {
i exists only inside this loop.
int id = nextId();
Another variable called id — local to the loop body.
printf("%d ", id);
Prints 101 102 103
}
printf("\ncalls = %d\n", calls);
Prints calls = 3
return 0;
}
Spot the bug
Mistakes almost every beginner makes — and how to fix them.
Using a variable outside its block
for (int i = 0; i < 5; i++) { }
printf("%d", i); // error
int i;
for (i = 0; i < 5; i++) { }
printf("%d", i); // 5
A variable declared in the for or in a block is gone after it: "'i' undeclared".
Expecting a local to remember its value
void count(void) {
int n = 0;
n++; // always 1
}
void count(void) {
static int n = 0;
n++; // 1, 2, 3 …
}
An ordinary local is created again on every call. static keeps it alive between calls.
Uninitialised local variables
int sum; sum += 5; // garbage + 5
int sum = 0; sum += 5;
Globals and statics start at 0, but locals contain whatever was in that memory before.
Shadowing by accident
int total = 0;
for (…) { int total = 0; total += x; }
// outer total is still 0
int total = 0;
for (…) { total += x; }
The inner int total is a new variable that hides the outer one; it vanishes after each round.
Using globals for everything
int a, b, result; // global
void add(void) { result = a + b; }
int add(int a, int b) { return a + b; }
Any function can change a global, so bugs are hard to find. Pass parameters and return values instead.
Check your understanding
Answer each question — you will see the explanation straight away.
Where can a local variable declared in main be used?
A local's scope is its own block.
What is the starting value of an uninitialised global int?
Globals and statics are set to 0 automatically; locals are not.
void f(void) { static int n = 0; n++; printf("%d ", n); } — f(); f(); f(); prints?
The static n is made once and keeps its value between calls.
int x = 1; { int x = 2; } printf("%d", x); prints?
The inner x was a separate variable that ended at }.
What does extern int count; do?
extern only declares; the variable is defined (memory reserved) in another place.
Scope, Lifetime & Storage Classes quiz
Test yourself: 10 questions, 70% to pass. Create a free account to take the quiz, save your progress and work towards the certificate.