C Variable Scope and Storage Classes
Updated 9 min read Aimerscrew
Every variable in C has a home. C variable scope decides where in your code a variable's name can be used, and its storage class decides how long the variable stays alive. In this lesson, you'll learn local, block and global scope, what happens when two variables share a name, and how the keywords auto, static, extern and register change a variable's behavior.
🌍 Real-life example
Think of a house. The front door number is visible to the whole street (a global variable). The kitchen has its own light switch that only works in the kitchen (a local variable). And a hand tally counter on the hallway shelf keeps its count even after you leave and come back (a static variable). Each room can even have something with the same name, like "the lamp", without any confusion.
What is variable scope in C?
The scope of a variable is the region of code where its name is visible. In C, scope is mostly decided by curly braces { }: a variable declared inside a pair of braces exists only from its declaration to the closing brace. A variable declared outside all functions can be used anywhere below it in the file.
A related idea is lifetime: how long the variable's memory exists while the program runs. Scope is about where you can use a name; lifetime is about when the value exists.
Block scope: variables inside braces
Any pair of braces creates a block: the body of an if, a loop or a function. A variable declared in a block has block scope. It disappears at the closing brace.
#include <stdio.h>
int main(void) {
int total = 10;
if (total > 5) {
int bonus = 3;
printf("Inside the block: total = %d, bonus = %d\n", total, bonus);
}
printf("Outside the block: total = %d\n", total);
return 0;
}
Inside the block: total = 10, bonus = 3
Outside the block: total = 10
Inside the if block, both variables are visible, because inner blocks can see variables from the blocks around them. After the closing brace, bonus is gone. If you tried printf("%d\n", bonus); after the if, GCC would stop with error: 'bonus' undeclared. The loop variable in for (int i = 0; ...) works the same way: it only exists inside the loop.
Local variables in functions
A function body is a block too, so variables declared inside a function are local to it. Two functions can use the same name, and the variables have nothing to do with each other:
#include <stdio.h>
void kitchen(void) {
int lights = 2;
printf("Kitchen lights on: %d\n", lights);
}
void bedroom(void) {
int lights = 1;
printf("Bedroom lights on: %d\n", lights);
}
int main(void) {
kitchen();
bedroom();
return 0;
}
Kitchen lights on: 2
Bedroom lights on: 1
Function parameters are local variables as well. They are created when the function is called and destroyed when it returns.
Global variables
A global variable is declared outside every function, usually near the top of the file. Every function below it can read and change it, and it lives for the whole run of the program.
#include <stdio.h>
int high_score = 0; /* global: visible to every function below */
void record_score(int score) {
if (score > high_score) {
high_score = score;
}
}
int main(void) {
record_score(120);
record_score(95);
record_score(240);
printf("High score: %d\n", high_score);
return 0;
}
High score: 240
⚠️ Use globals sparingly
Because any function can change a global, a bug that changes it can hide anywhere in the program. Prefer passing values as parameters and getting results back with return values. Use globals only for things that truly belong to the whole program.
What is variable shadowing?
When an inner block declares a variable with the same name as an outer one, the inner variable shadows (hides) the outer one until its block ends. The outer variable still exists; you just can't reach it by that name for a while.
#include <stdio.h>
int temperature = 20; /* global */
int main(void) {
printf("Global: %d\n", temperature);
int temperature = 25; /* local: hides the global */
printf("Local: %d\n", temperature);
{
int temperature = 30; /* inner block: hides both */
printf("Inner block: %d\n", temperature);
}
printf("Back in main: %d\n", temperature);
return 0;
}
Global: 20
Local: 25
Inner block: 30
Back in main: 25
C always uses the closest declaration. Shadowing is legal but confusing, so avoid reusing names on purpose. GCC can warn you about it if you add the -Wshadow flag.
What are storage classes in C?
A storage class is a keyword written before a declaration that tells C how long a variable lives and who can see it. There are four you'll meet: auto, static, extern and register.
| Storage class | Where you use it | Visible in | Lives for | Default value |
|---|---|---|---|---|
auto (the default) | Inside a block | That block | Until the block ends | Garbage (not set) |
static (local) | Inside a function | That function | Whole program | 0 |
static (global) | Outside functions | This file only | Whole program | 0 |
extern | Anywhere | Refers to a global defined elsewhere | Whole program | Set by its definition |
register | Inside a block | That block | Until the block ends | Garbage (not set) |
auto: the default for local variables
Every local variable is auto unless you say otherwise, so nobody writes the keyword. int count = 0; inside a function means the same as auto int count = 0;. An auto variable is created fresh each time its block starts and destroyed when it ends, so it can't remember anything between calls:
#include <stdio.h>
void count_visit(void) {
int visits = 0; /* auto: created again on every call */
visits++;
printf("Visits: %d\n", visits);
}
int main(void) {
count_visit();
count_visit();
count_visit();
return 0;
}
Visits: 1
Visits: 1
Visits: 1
static local variables: values that survive between calls
Add static to a local variable and it is created once, before the program starts, and lives until the program ends. Its initializer runs only one time. The name is still local to the function, but the value is remembered between calls, just like a tally counter keeps its count.
#include <stdio.h>
void click(void) {
static int count = 0; /* set to 0 only once */
count++;
printf("Tally: %d\n", count);
}
int main(void) {
click();
click();
click();
return 0;
}
Tally: 1
Tally: 2
Tally: 3
A practical use is handing out unique numbers, such as order IDs in an online shop, without a global variable that other code could change:
#include <stdio.h>
int next_order_id(void) {
static int last_id = 1000;
last_id++;
return last_id;
}
int main(void) {
printf("Order #%d\n", next_order_id());
printf("Order #%d\n", next_order_id());
printf("Order #%d\n", next_order_id());
return 0;
}
Order #1001
Order #1002
Order #1003
📝 Note
Global and static variables start at 0 automatically if you don't give them a value. Ordinary local (auto) variables do not: they hold whatever bytes were left in memory. Always initialize your locals.
#include <stdio.h>
int players_online; /* global: starts at 0 */
int main(void) {
static int saved_games; /* static: starts at 0 */
printf("%d %d\n", players_online, saved_games);
return 0;
}
0 0
static globals and static functions
Outside a function, static means something different: "private to this file". Once your program is split into several .c files, a static global or static function can't be seen from the other files. This prevents name clashes, like two files that both have a helper called log_message.
static int retry_limit = 3; /* only this .c file can use it */
static int is_valid_pin(int pin) { /* helper hidden from other files */
return pin >= 1000 && pin <= 9999;
}
extern: using a global defined elsewhere
extern declares a global variable without creating it. It says: "this variable exists, and its definition is somewhere else." It's mostly used with multiple files, which you'll learn in C multiple files. It also works within one file:
#include <stdio.h>
extern int lives; /* declaration: defined further down */
int main(void) {
printf("Lives: %d\n", lives);
return 0;
}
int lives = 3; /* definition: memory is created here */
Lives: 3
register: a historical hint
register int i; asks the compiler to keep a variable in a CPU register (very fast storage inside the processor). Decades ago this could speed up loops. Today's compilers choose registers far better than people do and mostly ignore the hint. The only real effect left is that you can't take the address of a register variable. You'll see it in old code, but you don't need to use it.
Common mistakes
1. Using a block variable after its block
for (int i = 0; i < 3; i++) {
int square = i * i;
}
printf("%d\n", square); /* error: 'square' undeclared */
Declare the variable before the block if you need it afterwards.
2. Expecting a local variable to remember its value
A plain local starts over on every call, as the count_visit() example showed. If a function must remember something between calls, make the variable static.
3. Reading an uninitialized local
int sum; /* no starting value */
sum = sum + 5; /* sum held garbage, so the result is garbage */
This prints an unpredictable garbage value. Write int sum = 0;.
4. Accidental shadowing
Declaring int total = 0; again inside a loop creates a new variable, so the outer total never changes. Use total = 0; (no type) to update the existing one.
Practice exercise
🏋️ Try it yourself
A parking garage hands out tickets. Write a function int next_ticket(void) that uses a static variable so the first call returns 1, the second returns 2, and so on. Call it four times from main and print Ticket 1 to Ticket 4.
Show solution
#include <stdio.h>
int next_ticket(void) {
static int number = 0;
number++;
return number;
}
int main(void) {
for (int i = 0; i < 4; i++) {
printf("Ticket %d\n", next_ticket());
}
return 0;
}
Ticket 1
Ticket 2
Ticket 3
Ticket 4
What's next?
You now know where variables live and how long they last, which is exactly what you need to understand a function that calls itself. Next up: recursion in C.
Key takeaways
- Scope is where a variable name can be used; lifetime is how long its value exists.
- A variable declared inside braces has block scope and disappears at the closing brace.
- Global variables are declared outside all functions and live for the whole program; use them sparingly.
- An inner variable with the same name shadows the outer one until its block ends.
- A static local variable is created once and keeps its value between function calls.
- static on a global or function makes it private to its file; extern declares a global defined elsewhere.
- Global and static variables start at 0; ordinary local variables start with garbage, so always initialize them.
Frequently asked questions
What is the difference between local and global variables in C?
A local variable is declared inside a function or block and can only be used there; it is destroyed when the block ends. A global variable is declared outside all functions, can be used by every function below it, and lives for the whole program.
What does static mean for a local variable in C?
A static local variable is created once and keeps its value between calls to the function. Its name is still only visible inside that function, and it starts at 0 if you do not initialize it.
What are the storage classes in C?
The common storage classes are auto (the default for local variables), static (keeps a value for the whole program, or makes a global private to its file), extern (refers to a global defined elsewhere) and register (an old speed hint that modern compilers mostly ignore).
Can a local variable have the same name as a global variable?
Yes. The local variable shadows the global one inside its block, so the name refers to the local variable there. It is legal but confusing, and GCC can warn about it with the -Wshadow flag.
Is the register keyword still useful in C?
Rarely. Modern compilers decide which variables to keep in CPU registers better than programmers can, so register is mostly ignored. Its only remaining effect is that you cannot take the address of a register variable.