C / FUNCTIONS
static functions and variables that remember
Keep state between calls with static locals, hide helper names inside one file with static at file scope, and tell the two meanings apart.
What you will learn
- Use a static local to carry state between calls without adding a global
- Explain why a static initialiser runs once but an assignment runs every call
- Mark file-local helpers static so their names cannot collide at link time
- Spot when a shared static buffer or counter makes a function unsafe to reuse
Understanding static functions and variables that remember
The keyword static answers two unrelated questions, and which one depends entirely on where you write it. Inside a function body it changes how long a variable lives: the object is created once before main starts and disappears only when the program ends, so whatever you left in it is still there on the next call. In front of a function or a variable at file scope it changes who can see the name: the identifier gets internal linkage, and no other translation unit can reach it no matter how it declares the name. Reading static correctly always begins with checking its position.
A static local remembers because it is not part of the frame the call builds. The compiler puts it with the program's other fixed-address objects and applies the initialiser once at program startup, which is why C insists that initialiser be a constant expression. So `static int n = 0;` is not an assignment that executes when control passes over it; it is a fact recorded about the object before the function ever runs, and that is exactly why n survives the return. The name is still local, though: only the body that declared it can mention n, so you get persistence without exposing the value to the rest of the program.
Marking a function static is about names, not about storage. A plain definition like `int clamp(int, int)` exports the symbol, so a second file with its own clamp collides at link time; with static on both, the two versions coexist because neither name leaves its own translation unit. It also tells the compiler that every call site is in this file, which is what lets it inline the body freely, discard it, or warn you that a helper is never used. Keep declarations consistent: a plain prototype followed by a static definition is a contradiction, because the prototype already handed the name external linkage.
<stdio.h>
static int static_count(void)
{
static int n = 0; /* one object, created before main, never destroyed */
n++;
return n;
}
static int auto_count(void)
{
int n = 0; /* a fresh object on every call */
n++;
return n;
}
int main(void)
{
for (int i = 1; i <= 4; i++) {
int s = static_count();
int a = auto_count();
printf("call %d: static=%d auto=%d\n", i, s, a);
}
return 0;
}
static means two different things: on a local it means one object initialised once and kept for the whole run, and at file scope it means the name is private to this file.
Worked examples
One-time setup remembered by a flag
A static local acts as a guard so expensive setup happens on the first call only.
<stdio.h>
static double table[5]; /* file scope + static = private to this file */
static void ensure_table(void)
{
static int built = 0;
if (built)
return;
for (int i = 0; i < 5; i++)
table[i] = 1.0 / (i + 1);
built = 1;
puts("table built");
}
static double reciprocal(int i)
{
ensure_table();
return table[i];
}
int main(void)
{
printf("%.3f\n", reciprocal(1));
printf("%.3f\n", reciprocal(4));
return 0;
}
Example explained
Line 1`static int built = 0;` has static duration, so the 1 written during the first call is still readable during the second.
Line 2The early return on the second call skips both the loop and puts, which is why the message appears once for two calls.
Line 3`static double table[5];` at file scope is zero-filled at startup and invisible to other files; the flag is what records that real values were written.
Line 4Dropping static from built would give a fresh zero each call and the table would be rebuilt every time.
A returned pointer into a static buffer
Returning the address of a static local is legal, but every call hands back the same storage.
<stdio.h>
static const char *bracketed(int n)
{
static char buf[8]; /* one buffer for the whole program */
snprintf(buf, sizeof buf, "<%d>", n);
return buf; /* safe: the buffer outlives the call */
}
int main(void)
{
const char *a = bracketed(7);
printf("a = %s\n", a);
const char *b = bracketed(42);
printf("a = %s b = %s\n", a, b);
printf("same object: %d\n", a == b);
return 0;
}
Example explained
Line 1`static char buf[8];` exists outside the call, so returning its address is fine; a plain `char buf[8]` would leave the caller with a dangling pointer.
Line 2The second call overwrites the same eight bytes, so a, which still points there, now reads <42> as well.
Line 3`a == b` prints 1 because there is exactly one buf for the entire program run.
Line 4A function shaped like this can only hold one useful result at a time, so the caller must copy the string before calling again.
Important notes
One object for the whole program also means one object shared by every recursive invocation and every thread, so a static counter touched by two threads is a data race; static provides storage, never synchronisation.
A static local with no initialiser starts at zero, unlike an automatic local whose value is indeterminate, and unlike C++ there is no lazy runtime initialisation to lean on.
Common mistakes
Writing `static int n;` and then adding `n = 0;` as the first statement: the assignment is executable code that runs on every call, so the counter resets each time and never gets past 1.
Initialising from a runtime value, as in `static int first = x;` or `static int cached = compute();`: a static local's initialiser must be a constant expression, so the file will not compile; use a flag and assign inside the body instead.
Declaring the prototype as `int helper(void);` but defining it as `static int helper(void)`: the prototype already gave the name external linkage, and gcc stops with "static declaration of 'helper' follows non-static declaration".
Try it yourself
Change, predict, then run
Write a static function `running_mean(double x)` that keeps a static sum and a static count and returns the mean of every value it has received so far. Call it with 2.0, 4.0 and 9.0 and print each returned mean with %.3f.
Open the C workspaceCheck your understanding
A function begins with `static int calls = 0;` and the very next line inside the body is `calls = 0;`. What happens on repeated calls, and why?
- It fails to compile, because a static local cannot be assigned to after its initialiser
- Nothing changes, because the compiler folds the redundant assignment into the one-time initialisation
- The counter never gets past 1, because the initialiser is applied once at startup while the assignment runs on every call
- The counter still accumulates, because assignments to an object with static duration take effect only on the first call
Show answer
The initialiser is part of how the object is set up before main runs, but `calls = 0;` is an ordinary statement in the body, so each entry wipes the value and the increment always starts from zero. Option 2 is tempting because the two lines look like duplicates, but the compiler cannot merge them: the assignment has a defined effect at every call, and removing it would change what the program prints.