Nearby lessons

111 of 124

C - static Storage Class

static is the most useful — and most confusing — storage class in C, because it does two different jobs. Inside a function it makes a variable survive between calls; at file scope it hides a name from other files.

The Two Meanings

In simple words: ask where the keyword is written. Inside a function, static answers "how long does it live?" — the whole program. At file scope, lifetime is already the whole program, so it answers a different question: "who can see it?" — only this file.
WrittenChangesEffect
Inside a functionLifetimeSurvives between calls
On a file-scope variableLinkagePrivate to this file
On a functionLinkageCallable only from this file
Example01
CCode Cell
1#include <stdio.h>
2 
3static int fileWide = 100; /* meaning 2: private to this file */
4 
5static void helper(void) /* meaning 3: private function */
6{
7 static int calls = 0; /* meaning 1: survives between calls */
8 calls++;
9 printf("helper called %d time(s), fileWide=%d\n", calls, fileWide);
10}
11 
12int main()
13{
14 helper();
15 helper();
16 helper();
17 return 0;
18}
Output
helper called 1 time(s), fileWide=100
helper called 2 time(s), fileWide=100
helper called 3 time(s), fileWide=100

A Static Local Remembers

The classic use — a counter that keeps its value without exposing a global:

Example02
CCode Cell
1#include <stdio.h>
2 
3int nextId(void)
4{
5 static int id = 0; /* initialised ONCE, not on every call */
6 id++;
7 return id;
8}
9 
10int main()
11{
12 printf("ID: %d\n", nextId());
13 printf("ID: %d\n", nextId());
14 printf("ID: %d\n", nextId());
15 printf("ID: %d\n", nextId());
16 return 0;
17}
Output
ID: 1
ID: 2
ID: 3
ID: 4

The Initialiser Runs Once

static int id = 0; does not reset id on every call. Reading the line as an ordinary assignment is the single most common misunderstanding of static. The initialisation happens once, before main starts — the line is effectively skipped on every call after the first.
Example03
CCode Cell
1#include <stdio.h>
2 
3void compare(void)
4{
5 int automatic = 10; /* assigned on EVERY call */
6 static int persistent = 10; /* initialised ONCE, before main */
7 
8 printf("automatic=%d persistent=%d\n", automatic, persistent);
9 
10 automatic += 5;
11 persistent += 5;
12}
13 
14int main()
15{
16 compare();
17 compare();
18 compare();
19 return 0;
20}
Output
automatic=10 persistent=10
automatic=10 persistent=15
automatic=10 persistent=20

Zero by Default

A static variable with no initialiser is zeroed. Unlike an automatic variable, this is guaranteed:

Example04
CCode Cell
1#include <stdio.h>
2 
3void demo(void)
4{
5 static int count; /* 0 */
6 static float total; /* 0.0 */
7 static char buffer[10]; /* all bytes 0 */
8 static int *ptr; /* NULL */
9 
10 count++;
11 printf("count=%d total=%.1f buffer=\"%s\" ptr=%s\n",
12 count, total, buffer, ptr ? "set" : "NULL");
13}
14 
15int main()
16{
17 demo();
18 demo();
19 return 0;
20}
Output
count=1 total=0.0 buffer="" ptr=NULL
count=2 total=0.0 buffer="" ptr=NULL

The Initialiser Must Be Constant

Because the value is set before the program runs, it cannot depend on anything computed at run time:

Example05
CCode Cell
1#include <stdio.h>
2 
3int getValue(void) { return 42; }
4 
5void demo(int param)
6{
7 static int ok = 10; /* constant - fine */
8 static int alsoOk = 5 * 2 + 3; /* constant expression - fine */
9 
10 /* static int bad1 = param; ERROR: not a constant */
11 /* static int bad2 = getValue(); ERROR: not a constant */
12 
13 static int lateInit = 0; /* the usual workaround */
14 if (lateInit == 0) lateInit = getValue();
15 
16 printf("ok=%d alsoOk=%d lateInit=%d\n", ok, alsoOk, lateInit);
17}
18 
19int main()
20{
21 demo(99);
22 demo(99);
23 return 0;
24}
Output
ok=10 alsoOk=13 lateInit=42
ok=10 alsoOk=13 lateInit=42

Scope Is Unchanged

A static local is still invisible outside its function. Only the lifetime changed — this is the difference between static and a global:

Example06
CCode Cell
1#include <stdio.h>
2 
3int globalCounter = 0; /* anyone can change this */
4 
5void safeCount(void)
6{
7 static int privateCounter = 0; /* only this function can touch it */
8 privateCounter++;
9 globalCounter++;
10 printf("private=%d global=%d\n", privateCounter, globalCounter);
11}
12 
13int main()
14{
15 safeCount();
16 globalCounter = 1000; /* meddling with the global */
17 safeCount();
18 
19 /* privateCounter = 1000; ERROR: out of scope - protected */
20 return 0;
21}
Output
private=1 global=1
private=2 global=1001

static at File Scope

A file-scope variable is shared across the whole program by default. Adding static makes it private, which is how C achieves encapsulation:

Example07
CCode Cell
1/* ---------- counter.c ---------- */
2#include <stdio.h>
3 
4static int count = 0; /* private - no other file can reach it */
5 
6void increment(void) { count++; }
7int getCount(void) { return count; }
8 
9/* ---------- main.c ---------- */
10/* extern int count; LINK ERROR: count is static in counter.c */
11 
12extern void increment(void);
13extern int getCount(void);
14 
15int main()
16{
17 increment();
18 increment();
19 increment();
20 printf("Count via the public function: %d\n", getCount());
21 return 0;
22}
Output
Count via the public function: 3

static Functions

Marking a helper static keeps it out of other files' reach, prevents name clashes at link time, and lets the compiler optimise it more aggressively:

Example08
CCode Cell
1#include <stdio.h>
2 
3/* Internal helpers - not part of this file's public interface */
4static int square(int x) { return x * x; }
5static int isPositive(int x) { return x > 0; }
6 
7/* The public function */
8int sumOfSquares(const int *a, int n)
9{
10 int total = 0, i;
11 for (i = 0; i < n; i++)
12 if (isPositive(a[i])) total += square(a[i]);
13 return total;
14}
15 
16int main()
17{
18 int values[5] = {1, -2, 3, -4, 5};
19 printf("Sum of squares of positives: %d\n", sumOfSquares(values, 5));
20 return 0;
21}
Output
Sum of squares of positives: 35

A Practical Use — One-Time Setup

A static flag makes a function initialise itself exactly once, however many times it is called:

Example09
CCode Cell
1#include <stdio.h>
2 
3void useResource(void)
4{
5 static int ready = 0;
6 
7 if (!ready)
8 {
9 printf("[setting up - happens once]\n");
10 ready = 1;
11 }
12 
13 printf("Using the resource\n");
14}
15 
16int main()
17{
18 useResource();
19 useResource();
20 useResource();
21 return 0;
22}
Output
[setting up - happens once]
Using the resource
Using the resource
Using the resource

Where static Goes Wrong

There is only ever one copy. That is the point of static, and also its danger. Recursive calls all share the same variable rather than getting their own, so a depth counter never unwinds. Two threads calling the same function race on it. And a function returning a pointer to a static buffer hands every caller the same memory — the second call silently overwrites the first result.
Example10
CCode Cell
1#include <stdio.h>
2 
3int badDepth(int n)
4{
5 static int depth = 0; /* shared by every recursive call */
6 depth++;
7 if (n <= 1) return depth;
8 return badDepth(n - 1);
9}
10 
11int goodDepth(int n, int depth)
12{
13 if (n <= 1) return depth; /* pass it as a parameter instead */
14 return goodDepth(n - 1, depth + 1);
15}
16 
17int main()
18{
19 printf("static depth, first call : %d\n", badDepth(3));
20 printf("static depth, second call: %d <- never reset!\n", badDepth(3));
21 printf("parameter depth : %d\n", goodDepth(3, 1));
22 printf("parameter depth again : %d <- correct\n", goodDepth(3, 1));
23 return 0;
24}
Output
static depth, first call : 3
static depth, second call: 6  <- never reset!
parameter depth          : 3
parameter depth again    : 3  <- correct

Common Mistakes

  • Expecting the initialiser to run every call — it runs once, before main.
  • Using a non-constant initialiser — a compile error; initialise lazily with a flag.
  • Using a static counter in a recursive function — it is shared, so it never unwinds.
  • Returning a pointer to a static local buffer — the next call overwrites the previous caller's data.
  • Using statics in multithreaded code — one shared copy means a data race.
  • Confusing the two meanings — lifetime inside a function, linkage at file scope.
  • Using static to make a global "safer" — it hides it from other files but not from the rest of this one.
Trainer's Note: a good rule is "static on functions and file-scope variables by default; static on locals only when you have a reason." The first two are pure benefit — they shrink the public surface of a file. The third introduces hidden state, and hidden state is where the awkward bugs live.
📝 Key Takeaways
  • Inside a function, static changes lifetime: the value survives between calls.
  • At file scope, static changes linkage: the name is private to that file.
  • A static variable is initialised once, before main runs, and defaults to zero.
  • Its initialiser must be a constant expression.
  • One shared copy makes it unsafe for recursion and threads.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4