Scope, Lifetime and static
Every variable has a scope — where in the code its name can be used — and a lifetime — how long its memory exists. Understanding both explains local and global variables, the many meanings of static, and a whole family of pointer bugs.
Block scope
A variable declared inside braces exists only from its declaration to the closing brace:
#include <stdio.h> int main(void) { int total = 0; for (int i = 1; i <= 3; i++) { int squared = i * i; // new each pass total += squared; } // i and squared don't exist here printf("total = %d\n", total); return 0; }
total = 14
Declare variables in the smallest scope that works. Fewer variables in view means fewer chances to use the wrong one.
Shadowing
An inner declaration with the same name hides the outer one. It's legal but confusing; -Wshadow warns about it:
#include <stdio.h> int main(void) { int count = 10; { int count = 99; printf("inner: %d\n", count); } printf("outer: %d\n", count); return 0; }
main.c:6:13: warning: declaration shadows a local variable [-Wshadow]
Automatic lifetime
Local variables have automatic storage: they're created when the block is entered and destroyed when it ends (they usually live on the stack). That's why a function must never return the address of one of its local variables — the memory is gone once the function returns:
#include <stdio.h> int *make_number(void) { int n = 42; return &n; } int main(void) { int *p = make_number(); printf("%d\n", *p); return 0; }
main.c:5:13: warning: address of stack memory associated with local variable 'n' returned [-Wreturn-stack-address]
Using such a "dangling" pointer is undefined behaviour — it may even print 42 by luck, then fail later. Return the value itself, use a caller-provided buffer, or allocate memory with malloc (lesson 36).
File scope: global variables
A variable declared outside any function has file scope and static lifetime: it exists for the whole run of the program, and starts at zero if not initialised.
#include <stdio.h> int requests_served; // global, starts at 0 void serve(void) { requests_served++; } int main(void) { serve(); serve(); serve(); printf("served %d requests\n", requests_served); return 0; }
served 3 requests
Globals are convenient but make programs harder to reason about: any function can change them, so bugs are hard to trace. Prefer passing values as parameters. Reasonable uses include program-wide constants and configuration set once at startup.
static local variables
static on a local variable gives it static lifetime: it's initialised once and keeps its value between calls, while staying private to the function:
#include <stdio.h> int next_ticket(void) { static int counter = 100; // initialised once, before the program starts return counter++; } int main(void) { for (int i = 0; i < 3; i++) { printf("Ticket %d\n", next_ticket()); } return 0; }
Ticket 100 Ticket 101 Ticket 102
static at file scope
On a global variable or function, static means something different: internal linkage — the name is private to this source file and can't be seen from other files. It's C's way of making things "private" in multi-file programs (lesson 52).
#include <stdio.h> static int cache_hits = 0; // only visible in this file static void record_hit(void) { // helper, private to this file cache_hits++; } int main(void) { record_hit(); record_hit(); printf("%d hits\n", cache_hits); return 0; }
2 hits
Summary
| Declared | Scope | Lifetime | Starts as |
|---|---|---|---|
| In a block | The block | Until the block ends | Indeterminate (garbage) |
static in a block | The block | Whole program | 0, or the initialiser (once) |
| Outside functions | The rest of the file (and other files via extern) | Whole program | 0, or the initialiser |
static outside functions | The rest of this file only | Whole program | 0, or the initialiser |
Allocated with malloc | Reached through pointers | Until free | Indeterminate (calloc: zero) |
Try it yourself
Write a function that returns a running average: each call adds one value and returns the average of all values so far, using static variables.
after 10: 10.00 after 20: 15.00 after 60: 30.00 after 30: 30.00