Scope, Linkage, and Storage Classes
What This Concept Is
Every name in a C program has three orthogonal attributes:
- Scope: where in the source the name is visible. Block scope (inside
{}), function prototype scope, file scope (outside any function). - Linkage: whether the name refers to the same entity across translation units.
externlinkage - the name is the same symbol across all files that declare it (e.g.,printf).- Internal linkage - the name is a distinct entity in this file only (via
staticat file scope). - No linkage - block-scope names, function parameters,
typedefnames.
- Storage duration: how long the object exists.
- Automatic - lives during the enclosing block.
- Static - lives for the whole program, initialized once.
- Allocated - lives from
malloctofree(not a storage-class specifier; covered in Module 2). - Thread - lives for the thread (C11
_Thread_local).
The storage-class specifiers auto, register, static, extern, and _Thread_local pick pieces of these.
Why It Matters Here
Once you have multi-file programs, these attributes decide:
- whether two files can see the same global variable or function
- whether a helper function is accidentally exported to the whole program
- whether a
staticlocal variable remembers its value between calls
Misunderstanding any one of these produces hard bugs, silent duplications, or giant symbol tables.
Concrete Example
/* util.c */
#include "util.h"
static int call_count = 0; /* internal linkage: only this file sees it */
static int helper(int x) { /* internal linkage: private helper */
return x * 2;
}
int public_add(int a, int b) { /* external linkage: declared in util.h */
call_count++;
return helper(a) + helper(b);
}
int get_call_count(void) {
return call_count;
}
/* counter.c */
int counter_inside(void) {
static int n = 0; /* static storage, block scope, no linkage */
n++;
return n; /* survives across calls */
}
static means three different things here: file-scope static int call_count (internal linkage), block-scope static int n (static storage duration), file-scope static int helper(...) (internal linkage for a function). The keyword is overloaded.
Common Confusion / Misconception
"static means constant." No. static is about either storage duration (keeps value across calls) or linkage (hides from other files). Constants use const.
"extern int x; defines x." No. It declares that x exists somewhere. The definition is the single int x = 0; (or int x;) at file scope in exactly one .c file.
"register makes it faster." In practice no. Modern compilers ignore register as an optimization hint. Its only remaining effect is that you cannot take the address of a register variable.
How To Use It
- Mark file-scope helpers
staticunless they need to be callable from other files. - Put
externdeclarations of shared globals in a header; put the single definition in one.cfile. - Use block-scope
staticfor small per-function caches (e.g., a one-time initialization flag). - Avoid
register. Write clear code and trust the optimizer. - Treat any non-
staticfile-scope variable as part of your module's public surface area.
Check Yourself
- What does
staticmean on a file-scope function? On a block-scope variable? - What is the difference between
extern int x;in a header andint x;in a.cfile? - If two
.cfiles both defineint foo;at file scope withoutstatic, what happens at link time?
Mini Drill or Application
Write a two-file program that:
- Declares
int request_count;with external linkage instats.cand exposes it viaextern int request_count;instats.h. - Uses a
staticfile-scope functionstatic void normalize(char *s)insidestats.cthat is not visible tomain.c. - Uses a block-scope
static int last_id = 0;inside a functionint next_id(void)to give each call a new id. - Confirm with
nm stats.othat the static helpers are local and the public function is global.