liveBook · Manning

Storage duration

As explained in Modern C

Storage duration is the C property that specifies how long an object exists during program execution and how its storage is managed.

Listing 13.2. An example of shadowing with an extern variable
 1   #include <stdio.h>
 2
 3   unsigned i = 1;
 4
 5   int main(void) {
 6     unsigned i = 2;        /* A new object */ 
 7     if (i) {
 8       extern unsigned i;   /* An existing object */ 
 9       printf("%u\n", i);
10     } else {
11       printf("%u\n", i);
12     }
13   }


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":1},{\"line\":0,\"ch\":2}],[{\"line\":2,\"ch\":18},{\"line\":2,\"ch\":19}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":1}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":1}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":1}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":1}],[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":13}],[{\"line\":1,\"ch\":1},{\"line\":1,\"ch\":2}],[{\"line\":5,\"ch\":20},{\"line\":5,\"ch\":21}],[{\"line\":11,\"ch\":1},{\"line\":11,\"ch\":2}],[{\"line\":2,\"ch\":1},{\"line\":2,\"ch\":2}],[{\"line\":12,\"ch\":1},{\"line\":12,\"ch\":2}],[{\"line\":2,\"ch\":5},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":15}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":24}],[{\"line\":3,\"ch\":1},{\"line\":3,\"ch\":2}],[{\"line\":4,\"ch\":1},{\"line\":4,\"ch\":2}],[{\"line\":4,\"ch\":5},{\"line\":4,\"ch\":8}],[{\"line\":8,\"ch\":11},{\"line\":8,\"ch\":14}],[{\"line\":10,\"ch\":11},{\"line\":10,\"ch\":14}],[{\"line\":4,\"ch\":9},{\"line\":4,\"ch\":13}],[{\"line\":4,\"ch\":14},{\"line\":4,\"ch\":18}],[{\"line\":5,\"ch\":1},{\"line\":5,\"ch\":2}],[{\"line\":2,\"ch\":5},{\"line\":2,\"ch\":13}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":15}],[{\"line\":7,\"ch\":16},{\"line\":7,\"ch\":24}],[{\"line\":6,\"ch\":1},{\"line\":6,\"ch\":2}],[{\"line\":6,\"ch\":7},{\"line\":6,\"ch\":9}],[{\"line\":7,\"ch\":1},{\"line\":7,\"ch\":2}],[{\"line\":7,\"ch\":9},{\"line\":7,\"ch\":24}],[{\"line\":8,\"ch\":1},{\"line\":8,\"ch\":2}],[{\"line\":8,\"ch\":9},{\"line\":8,\"ch\":15}],[{\"line\":10,\"ch\":9},{\"line\":10,\"ch\":15}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":2}],[{\"line\":9,\"ch\":9},{\"line\":9,\"ch\":13}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":2}],[{\"line\":8,\"ch\":9},{\"line\":8,\"ch\":15}],[{\"line\":10,\"ch\":9},{\"line\":10,\"ch\":15}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":2}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":2}]]"}
!@%STYLE%@!

Trace the two objects named `i`: the local object is 2, so execution enters the `if` block. There, `extern unsigned i` refers to the file-scope object, whose value is 1. The program prints 1. section 13.2

The name visible at a point in a program does not, by itself, tell you how long its object exists. Storage duration concerns an object's storage; scope concerns where an identifier is visible. A declaration can refer to an existing object, while a definition creates a distinct one. section 13.2

The distinctions matter when choosing how to create and initialize an object:

  • Static storage lasts for the whole execution. An `extern` declaration can designate a static-duration object defined at file scope, including in another translation unit. section 13.2
  • A local variable in a recursive function has a separate storage instance for each call. Do not mistake repeated use of its name for reuse of one instance. section 13.0
  • Dynamic allocation creates storage that must be tracked and freed. Allocated storage, like automatic storage, needs explicit initialization; static and thread-storage-duration objects receive default initialization. section 13.0 section 13.4

TAKEAWAY: Visibility and usability are different. A nested declaration may shadow a local name without making the earlier object unusable: a pointer obtained before the shadowing can still be passed to a function that changes that object. section 13.2

Table 13.1. Storage classes, scope, linkage of identifiers, and storage duration of the associated objects Tentative indicates that a definition is implied only if there is no other definition with an initializer. Induced indicates that the linkage is internal if another declaration with internal linkage has been met prior to that declaration; otherwise, it is external. (view table figure)
Class Scope Definition Linkage Duration Lifetime
Initialized File Yes External Static Whole execution
extern, initialized File Yes External Static Whole execution
Compound literal File Yes N/A Static Whole execution
String literal Any Yes N/A Static Whole execution
static, initialized Any Yes Internal Static Whole execution
Uninitialized File Tentative External Static Whole execution
extern, uninitialized Any No Induced Static Whole execution
static, uninitialized Any Tentative Internal Static Whole execution
thread_local File Yes External Thread Whole thread
extern thread_local Any No External Thread Whole thread
static thread_local Any Yes internal Thread Whole thread
Compound literal     N/A    
Non-VLA     None    
Non-VLA, auto Block Yes None Automatic Block of definition
register     None    
VLA Block Yes None Automatic From definition to end of block
Function return with array Block Yes None Automatic To the end of expression

Use the table to compare a declaration's storage class, scope, linkage, storage duration, and lifetime rather than treating those properties as interchangeable. section 13.2

Initialization is a practical consequence of the duration distinction. Static and thread-storage-duration objects default to zero-initialized members. For automatic or allocated objects, establish a well-defined state explicitly; for allocated data, a type-specific initialization function can keep allocation and initialization together. section 13.4

How Modern C covers it

How deep it goes
Works through shadowing and extern declarations, compares storage categories, and develops initialization rules; defers finer object-lifetime and machine-stack details.
Examples
Uses shadowed local variables, an extern declaration that selects a file-scope object, and initialization functions for allocated data.
What it assumes
Assumes familiarity with C declarations, pointers, and function calls while explicitly distinguishing identifier visibility from object usability.
What you can do after
Diagnose/debug with it by separating identifier shadowing from object identity and deciding which objects require explicit initialization.
How it connects
Connects storage duration to object lifetime, scope, linkage, dynamic allocation, recursion, and initialization.

Also explained in

Compare how 2 books explain storage duration →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C. The section links point to the parts of the book it draws on.