liveBook · Manning

Lifetime

As explained in Modern C

Object lifetime is the interval during program execution in which a C object exists and may be accessed according to the language rules.

A local declaration can hide a name without ending the object behind it. In the shadowing example, an inner `int x` hides an outer `double x`, yet a pointer saved earlier still lets `squareIt` modify the outer object. Visibility of an identifier and lifetime of an object are different questions. section 13.2

Listing 13.1. An example of shadowing with local variables
 1     void squareIt(double* p) {
 2       *p *= *p;
 3     }
 4     int main(void) {
 5     double x = 35.0;
 6     double* xp = &x;
 7     {
 8       squareIt(&x);  /* Refers to double x */ 
 9       ...
10       int x = 0;     /* Shadow double x */ 
11       ...
12       squareIt(xp);  /* Valid use of double x */ 
13       ...
14     }
15     ...
16     squareIt(&x);    /* Refers to double x */ 
17     ...
18   }


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":1},{\"line\":0,\"ch\":2}],[{\"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\":13,\"ch\":0},{\"line\":13,\"ch\":1}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":1}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":1}],[{\"line\":16,\"ch\":0},{\"line\":16,\"ch\":1}],[{\"line\":17,\"ch\":0},{\"line\":17,\"ch\":1}],[{\"line\":0,\"ch\":7},{\"line\":0,\"ch\":11}],[{\"line\":3,\"ch\":16},{\"line\":3,\"ch\":20}],[{\"line\":0,\"ch\":21},{\"line\":0,\"ch\":27}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":13}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":13}],[{\"line\":7,\"ch\":37},{\"line\":7,\"ch\":43}],[{\"line\":9,\"ch\":34},{\"line\":9,\"ch\":40}],[{\"line\":11,\"ch\":40},{\"line\":11,\"ch\":46}],[{\"line\":15,\"ch\":37},{\"line\":15,\"ch\":43}],[{\"line\":1,\"ch\":1},{\"line\":1,\"ch\":2}],[{\"line\":11,\"ch\":1},{\"line\":11,\"ch\":2}],[{\"line\":2,\"ch\":1},{\"line\":2,\"ch\":2}],[{\"line\":4,\"ch\":18},{\"line\":4,\"ch\":19}],[{\"line\":12,\"ch\":1},{\"line\":12,\"ch\":2}],[{\"line\":3,\"ch\":1},{\"line\":3,\"ch\":2}],[{\"line\":13,\"ch\":1},{\"line\":13,\"ch\":2}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":10}],[{\"line\":9,\"ch\":9},{\"line\":9,\"ch\":12}],[{\"line\":3,\"ch\":11},{\"line\":3,\"ch\":15}],[{\"line\":0,\"ch\":7},{\"line\":0,\"ch\":11}],[{\"line\":3,\"ch\":16},{\"line\":3,\"ch\":20}],[{\"line\":4,\"ch\":1},{\"line\":4,\"ch\":2}],[{\"line\":14,\"ch\":1},{\"line\":14,\"ch\":2}],[{\"line\":0,\"ch\":21},{\"line\":0,\"ch\":27}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":13}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":13}],[{\"line\":7,\"ch\":37},{\"line\":7,\"ch\":43}],[{\"line\":9,\"ch\":34},{\"line\":9,\"ch\":40}],[{\"line\":11,\"ch\":40},{\"line\":11,\"ch\":46}],[{\"line\":15,\"ch\":37},{\"line\":15,\"ch\":43}],[{\"line\":5,\"ch\":1},{\"line\":5,\"ch\":2}],[{\"line\":15,\"ch\":1},{\"line\":15,\"ch\":2}],[{\"line\":0,\"ch\":21},{\"line\":0,\"ch\":27}],[{\"line\":4,\"ch\":7},{\"line\":4,\"ch\":13}],[{\"line\":5,\"ch\":7},{\"line\":5,\"ch\":13}],[{\"line\":7,\"ch\":37},{\"line\":7,\"ch\":43}],[{\"line\":9,\"ch\":34},{\"line\":9,\"ch\":40}],[{\"line\":11,\"ch\":40},{\"line\":11,\"ch\":46}],[{\"line\":15,\"ch\":37},{\"line\":15,\"ch\":43}],[{\"line\":6,\"ch\":1},{\"line\":6,\"ch\":2}],[{\"line\":16,\"ch\":1},{\"line\":16,\"ch\":2}],[{\"line\":7,\"ch\":1},{\"line\":7,\"ch\":2}],[{\"line\":17,\"ch\":1},{\"line\":17,\"ch\":2}],[{\"line\":8,\"ch\":1},{\"line\":8,\"ch\":2}],[{\"line\":9,\"ch\":0},{\"line\":9,\"ch\":2}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":10}],[{\"line\":9,\"ch\":9},{\"line\":9,\"ch\":12}],[{\"line\":10,\"ch\":0},{\"line\":10,\"ch\":2}],[{\"line\":11,\"ch\":0},{\"line\":11,\"ch\":2}],[{\"line\":12,\"ch\":0},{\"line\":12,\"ch\":2}],[{\"line\":13,\"ch\":0},{\"line\":13,\"ch\":2}],[{\"line\":14,\"ch\":0},{\"line\":14,\"ch\":2}],[{\"line\":15,\"ch\":0},{\"line\":15,\"ch\":2}],[{\"line\":16,\"ch\":0},{\"line\":16,\"ch\":2}],[{\"line\":17,\"ch\":0},{\"line\":17,\"ch\":2}]]"}
!@%STYLE%@!

Follow `xp` through the inner block: it still points to the outer `double x` while the name `x` denotes the inner `int`. section 13.2

TAKEAWAY: An object has a lifetime with a beginning and an end. Accessing it outside that interval has undefined behavior. Where its lifetime begins and ends depends on how its storage is created, not merely on whether a name is visible. section 13.2

C distinguishes four storage durations:

  • Static: file-scope objects and block-scope variables declared `static` live for the whole program execution. They are initialized before application code runs, even if their identifiers have narrow visibility. section 13.2.1
  • Automatic: a local object’s lifetime follows its relevant scope during execution. Entering that scope starts an ordinary automatic object’s lifetime; a new loop iteration defines a new instance of a local object. section 13.3 section 17.3
  • Allocated: calls such as `malloc` explicitly determine storage duration. section 13.2
  • Thread: storage duration is bound to a thread of execution. section 13.2

A pointer can retain an address after the object there has died; retaining the address does not extend the lifetime. For a compound literal created as the body of a `while` statement, dereferencing a saved pointer after the loop is invalid. Repeating an assignment by `goto` within the surrounding block instead leaves the compound-literal object alive until that block is left. These forms are not interchangeable. section 17.3

1   size_t* ip = 0
2   while(something)
3     ip = &(size_t){ fun() };        /* Life ends with while    */
4                                     /* Good: resource is freed */
5   printf("i is %d", *ip)            /* Bad: object is dead     */


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":1}],[{\"line\":0,\"ch\":4},{\"line\":0,\"ch\":10}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":19}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":1}],[{\"line\":1,\"ch\":4},{\"line\":1,\"ch\":9}],[{\"line\":2,\"ch\":56},{\"line\":2,\"ch\":61}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":1}],[{\"line\":0,\"ch\":4},{\"line\":0,\"ch\":10}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":19}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":1}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":1}],[{\"line\":4,\"ch\":4},{\"line\":4,\"ch\":10}]]"}
!@%STYLE%@!

The `while` version saves an address, then attempts to read the compound literal after its lifetime has ended. section 17.3

1   size_t* ip = 0
2   RETRY:
3     ip = &(size_t){ fun() };        /* Life continues           */
4   if (condition) goto RETRY;
5                                     /* Bad: resource is blocked */
6   printf("i is %d", *ip)            /* Good: object is alive    */


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":0},{\"line\":0,\"ch\":1}],[{\"line\":0,\"ch\":4},{\"line\":0,\"ch\":10}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":19}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":1}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":1}],[{\"line\":0,\"ch\":4},{\"line\":0,\"ch\":10}],[{\"line\":2,\"ch\":13},{\"line\":2,\"ch\":19}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":1}],[{\"line\":2,\"ch\":42},{\"line\":2,\"ch\":44}],[{\"line\":3,\"ch\":4},{\"line\":3,\"ch\":6}],[{\"line\":3,\"ch\":19},{\"line\":3,\"ch\":23}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":1}],[{\"line\":5,\"ch\":0},{\"line\":5,\"ch\":1}],[{\"line\":5,\"ch\":4},{\"line\":5,\"ch\":10}]]"}
!@%STYLE%@!

Compare the `goto` version: the compound literal remains alive at the final read, though its resources remain occupied longer. section 17.3

TAKEAWAY: Do not infer lifetime solely from the point at which a definition is executed. A `goto` example revisits a compound literal and obtains equal, valid pointers on successive passes; its specified result depends on the enclosing block’s lifetime rule, not on a fresh object being created at each jump. section 13.3

How Modern C covers it

How deep it goes
Defines the access rule and distinguishes storage durations, then works through shadowing, loop, and goto cases; defers concrete machine models.
Examples
Uses a shadowed local variable, a revisited compound literal, and contrasting while and goto snippets.
What it assumes
Assumes familiarity with declarations, blocks, pointers, and compound literals; introduces goto and labels where the lifetime example needs them.
What you can do after
Diagnose/debug with it whether a saved pointer still refers to a live object after a loop or jump.
How it connects
Connects lifetime to storage duration, identifier visibility, compound literals, pointers, and control flow.

Also explained in

Compare how 8 books explain lifetime →

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.