If part 15 helped you prepare code with the preprocessor, it's time to dissect the question "When is a variable created, and when does it disappear?" In C, variables that share the same name can have completely different lifetimes depending on their storage duration and scope. Add linkage to the mix, and you must separately reason about "How long does it stay alive?", "Where is it visible?", and "Does the same name connect across translation units?" A translation unit is roughly one .c file after the preprocessor has expanded its headers. Mastering this flow prevents the misuse of globals and keeps sharing variables between different .c files from becoming chaos.
New Terms in This Lesson
- Storage Duration: How long the value stays in memory
- Automatic Storage: The lifetime of local variables that vanish once you leave the block
- Static Storage: Variables that live for the entire program run
- Scope: The region of code where a name can be referenced
Key Ideas
Study Notes
- Estimated time: 60 minutes
- Prereqs: Functions, preprocessing, header/source separation
- Goal: Use
staticandexternto control storage duration, and distinguish block/file scope
Storage duration and scope must be discussed together.
- Storage duration describes how long the backing memory remains alive. The main categories are automatic, static, and dynamic.
- Scope describes the region of code that can refer to that name.
- Linkage captures whether the same name points to the same entity from other files.
They are not always synchronized. A static variable inside a function has static storage duration (kept for the entire program) but scope limited to that function. So far, most variables have lived inside one function. What if you need a variable to remain available across calls, or even across files? That is where file scope, linkage, and extern begin to matter. This article covers:
- Automatic variables and block scope
- Global variables and file scope
- Internal linkage via the
statickeyword - Reaching for global variables from other files with
extern - Examples and tips for static locals
Code Walkthrough
Automatic Storage and Block Scope
#include <stdio.h>
int main(void) {
int count = 0; // automatic storage duration
if (1) {
int count = 10; // new block scope
printf("block count = %d\n", count);
}
printf("outer count = %d\n", count);
return 0;
}
Declaring the same name inside the if block gives it a brand-new lifetime that ends once the block finishes. Automatic variables release their storage when the function returns, so do not hand out their addresses.
Global Variables and File Scope
#include <stdio.h>
int g_total = 0; // file scope + static storage duration
void add_score(int value) {
g_total += value;
}
int main(void) {
add_score(10);
add_score(5);
printf("total = %d\n", g_total);
return 0;
}
File-scope variables can be referenced anywhere below their declaration and keep their value for the entire run. Still, global variables increase coupling, so prefer passing data through parameters or structs unless a global truly makes sense.
Using static for Internal Linkage
// counter.c
#include <stdio.h>
static int internal_total = 0; // hidden from other translation units
void increase(void) {
internal_total++;
printf("internal_total = %d\n", internal_total);
}
Adding static to a file-scope variable or function gives it internal linkage, so the symbol does not participate in linking from other translation units. It is useful when you want to keep internal state or helper functions off the public header surface.
static changes slightly based on context:
- File scope
static: hides the symbol from other files - Function-scope
static: keeps the value alive even after the function returns
Static Local Variables
#include <stdio.h>
void greet(void) {
static int called = 0; // static storage + block scope
called++;
printf("greet called %d times.\n", called);
}
int main(void) {
greet();
greet();
greet();
return 0;
}
Static locals preserve their value across calls. Use them for call counters, caches, or lazy initialization. Just remember to guard against concurrent access in multithreaded code.
Reaching External Variables with extern
// config.c
int g_mode = 1;
// main.c
#include <stdio.h>
extern int g_mode; // declared, not defined, in this file
int main(void) {
if (g_mode == 1) {
printf("standard mode\n");
}
return 0;
}
An extern declaration says "This variable is defined elsewhere; treat this as a reference to it." The declaration itself does not allocate new storage. Instead, it points to a definition that must exist in exactly one translation unit. The usual pattern is to put extern declarations in headers and keep the single definition in a .c file.
Storage Duration Cheat Sheet
| Storage duration | Keyword/example | Lifetime | Scope sample |
|---|---|---|---|
| Automatic | Inside a function | Until the block ends | Block scope |
| Static | Globals, static locals | Entire program | File or block scope |
| Dynamic | malloc/free |
Until free |
Wherever the pointer travels |
Keep this table in mind so that every time you declare a variable, you also ask "Who holds onto it, and until when?"
Why It Matters
- Understanding storage duration prevents memory bugs and uncontrolled global state.
staticandexternhelp set module boundaries and encapsulate state.- As you move toward function pointers and build automation, a firm grasp of variable lifetimes shortens debugging time.
Practice in CodeSandbox
The sandbox below uses CodeSandbox's Universal starter. For C, the key learning loop is still compile and run in the terminal, so recreate the lesson code as a source file and repeat that cycle directly.
💬 댓글
이 글에 대한 의견을 남겨주세요