C global variables, scope, variable lifetime, and the static keyword explained.
π What to learn on this page
β Must-know essentials
Declared outside any function β global
Visible and writable from every function
A local variable shadows a same-name global
β Read if you have time
static hides from other files
extern references from other files
Avoid overusing globals
Scope β where a variable is visible
The region where a variable can be referenced β its scope β is determined by where you declare it.
intmain(void) {
int a = 10; // valid inside mainif (a > 0) {
int b = 20; // valid only inside this blockprintf("%d\n", a+b); // OK
}
// printf("%d\n", b); β error! b is out of scope
}
Local variable
Valid only inside the function or block. Destroyed on exit.
Global variable
Declared outside any function. Accessible from everywhere.
static variable
A local variable that keeps its value between calls.
Variable lifetime β when it's born and when it dies
Scope tells you where a variable is visible. Lifetime tells you when it's allocated in memory and when it gets freed.
Three kinds of lifetime
Kind
Where declared
Created when
Destroyed when
auto (local)
inside a function/block
when the block is entered
when the block is exited
global
outside any function
program start
program end
static
inside a function (with static)
program start
program end
voidexample(void) {
int a = 1; // auto: created per call, gone on exitstaticint b = 1; // static: created once, value persists
a++; b++;
printf("a=%d b=%d\n", a, b);
}
intmain(void) {
example(); // a=2 b=2example(); // a=2 b=3 β a resets, b persistsexample(); // a=2 b=4
}
Visualize the lifetime
Click the button to watch how each variable changes as example() is called three times.
Step 0 / 9
Call state
Variables in memory
Output
Global variables β shared across every function
#include<stdio.h>int counter = 0; // global variable (outside main, etc.)voidincrement(void) {
counter++; // usable without any argument
}
intmain(void) {
increment();
increment();
increment();
printf("%d\n", counter); // β 3
}
Don't overuse them: globals are convenient, but it's hard to track down where they get modified, so they tend to invite bugs. Use them sparingly.
static variables β persist across calls
A regular local variable is destroyed as soon as the function returns, but adding static makes it live until the program ends.
voidcount(void) {
staticint n = 0; // initialized to 0 only once
n++;
printf("Call count: %d\n", n);
}
intmain(void) {
count(); // β 1count(); // β 2count(); // β 3
}
When to use it: when you want to count how many times a function has been called, or remember a previous result β especially when you need state without the risks of a full global.
Memory layout β where do variables actually live?
Local, global, and static variables are stored in different memory regions. That's the real reason their lifetimes differ.
Memory map of a C program
When a C program runs, memory is divided into five regions:
High address β
π Stack
Local variables, function arguments, and return addresses. Automatically allocated and freed with each function call.
β grows down β
(free space)
β grows up β
π§± Heap
Dynamically allocated via malloc / free. Managed explicitly by the programmer.
π BSS (Uninitialized Data)
Uninitialized global and static variables. Automatically zeroed at program start.
πΎ Data (Initialized)
Initialized global and static variables. Live for the entire program.
π Text (Code)
Machine code and string literals. Read-only.
Low address β
Code mapped to regions
#include<stdio.h>#include<stdlib.h>int g_init = 100; // πΎ Data segment (initialized)int g_uninit; // π BSS (uninitialized β auto-zeroed)voidfunc(void) {
int local = 5; // π Stack (new copy per call)staticint s = 0; // πΎ Data segment (created once)int *heap = malloc(sizeof(int)); // π§± Heap
*heap = 42;
free(heap);
}
intmain(void) {
func();
return0;
}
Variable types and their memory regions
Variable type
Declared in
Memory region
Created
Initial value
Local (auto)
Inside function
π Stack
On function call
Undefined (garbage)
Local (static)
Inside function + static
πΎ Data / BSS
At program start
0 or specified value
Global (initialized)
Outside any function
πΎ Data
At program start
Specified value
Global (uninitialized)
Outside any function
π BSS
At program start
0 (automatic)
malloc'd
Dynamic inside function
π§± Heap
On malloc call
Undefined (calloc: 0)
String literal
Source code "..."
π Text
At program start
Read-only
Function calls and the stack
Each function call pushes a stack frame. When the function returns, the frame is popped automatically.
voidb(void) { int y = 20; } // b's framevoida(void) { int x = 10; b(); } // a's frameintmain(void) { a(); return0; }
Stack when main β a() β b() is in progress:
b() frame (y=20) β top (pushed last)
a() frame (x=10)
main() frame
When b() returns, its frame is popped, then a() returns and its frame is popped too.
What is a stack overflow? Too much recursion or deeply nested calls can exhaust the stack region, triggering a segmentation fault.
Lifetimes, explained through memory regions
Short-lived = Stack
Local variables. Disappear the moment the function ends. Fast, but they can't persist across calls.
Long-lived = Data/BSS
Globals and statics. Live for the whole program. Always accessible, but overusing them hurts maintainability.
Flexible = Heap
Allocated with malloc and freed with free. You control both size and lifetime. Forget to free, and you get a memory leak.
Try it yourself β scope
scope.c
Output
Click Run...
π‘ Try these ideas too
Modify a global variable from several different functions
Use a static local variable to track the number of calls
Shadow a global with a local variable of the same name