Skip to content
VLSI Mentor

C Part I · Module 9 · Memory and Object Lifetime

The Stack and the Heap as Mental Models

Ask a working C programmer where a local variable lives and the answer comes back in one word: the stack. Ask where a malloc block lives and the answer is the heap. Neither word appears in the C standard, and the previous three chapters managed to describe every lifetime rule without either of them — because the rule really is about time rather than place, and a reader who learns the regions first tends to reason about memory maps when they should be reasoning about lifetimes. But every working C programmer talks about the stack and the heap, every explanation you will read elsewhere uses those words, and they describe something real and stable about how implementations behave. Refusing to teach them would be a different kind of disservice. This chapter gives you both, with the one line that matters drawn clearly: what follows is an accurate account of what implementations do, and none of it is what the standard promises.

Intermediate17 min readCPart IFundamentalsMemory

Part I · Module 9 · Chapter 9.4 · The Stack and the Heap as Mental Models

1. Why These Words Were Withheld

Chapter 5.7 built the frame model, used it to explain calls, returns and recursion, and then spent its final section insisting that C never mentions a stack — what the language specifies is a lifetime rule, which Chapter 9.1 then supplied.

That ordering was a deliberate bet. A reader who meets the regions first reasons about memory maps when the question is really about lifetimes, and ends up believing things like it is still on the stack, so it is still valid. With Chapter 9.1's rule already in place, the regions can be handed over freely, because you now have something to check them against.

So, plainly:

The stack and the heap are not in the C standard. They are names for how essentially every hosted implementation arranges storage. The model is accurate, it is worth having, and it answers a different question from the one Chapter 9.1 answered.

QuestionAnswer comes from
Is this program's behaviour defined?lifetime — Chapter 9.1
Why is this cheap, and that expensive?the regions — this chapter
How deep can recursion go?the regions
Will this pointer still work?lifetime, never the regions

2. The Model

Four regions on the left, each paired with what it holds on the right. The static area is set up before main and never moves, and holds objects with static storage duration — literals, file-scope objects and block statics. The heap grows as you allocate, and holds objects with allocated storage duration, which is what malloc returns. Between them is unused space, into which the two sides grow toward each other. The stack grows as calls nest, and holds objects with automatic storage duration — locals and parameters.static areaset up before main · never movesthe heapgrows as you allocateunused space between themhow much is not a C questionthe stackgrows as calls neststatic durationliterals, file-scope, block statics ·9.3allocated durationwhat malloc returns · 9.6nothing yetthe two sides grow toward each otherautomatic durationlocals and parameters · 9.2holdsholdsholds12
Figure 1 — the arrangement essentially every hosted implementation uses, and what each part holds. Objects with static storage duration are placed in an area set up before main runs and never moved. Allocated objects come from the heap, which grows as you ask for more. Automatic objects live in frames on the stack, which grows and shrinks as calls nest and return. The two growing areas are separated by space neither has claimed. None of this is specified by C; it is what implementations do, and the mapping from storage duration to region is the useful half.

The stack holds a frame per call in progress — Chapter 5.7's picture, now with its name. Frames are pushed and popped in strict order, which is why creating an automatic object costs essentially nothing: the space is claimed by adjusting one number.

The heap is a region managed by a library routine that hands out blocks on request and takes them back. It is not ordered, blocks can be released in any sequence, and keeping track of what is free is real work — which is why allocation costs more than a local.

The static area is prepared before main begins and never changes size. Chapter 9.3's zero-initialisation is arranged here, before any of your code runs.

3. What You Can Actually Observe

Azvya Education Pvt. Ltd.VLSI Mentor
where things end up
#include <stdio.h>

static int static_object = 1;

void inner(void)
{
    int deep = 3;
    printf("  automatic in inner()  %p\n", (void *)&deep);
}

void outer(void)
{
    int shallow = 2;
    printf("  automatic in outer()  %p\n", (void *)&shallow);
    inner();
}

int main(void)
{
    int in_main = 0;

    printf("static object           %p\n", (void *)&static_object);
    printf("string literal          %p\n", (void *)"literal");
    printf("automatic in main()     %p\n", (void *)&in_main);
    outer();

    return 0;
}

One run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
static object           0x104654000
string literal          0x10464c670
automatic in main()     0x16b7b2778
  automatic in outer()  0x16b7b274c
  automatic in inner()  0x16b7b272c

Chapter 7.1 established that the absolute numbers differ on every run, and they do. What is stable is the structure, and three things in it are worth reading.

The static object and the string literal are near each other, and both are far from everything else. They are in the same prepared area, exactly as Chapter 9.3 would predict.

The automatic objects are somewhere else entirely — a completely different part of the address space.

The automatic addresses decrease as calls nest. main is highest, outer lower, inner lowest. Each new frame is placed below the last, which is what "the stack grows downward" means — and that direction is a property of this platform, not of C. Implementations where it grows upward exist.

Chapter 9.6 adds a fourth line to this program, for an allocated object, and it lands somewhere different again.

4. What the Model Is Good For

Predicting cost. An automatic object is nearly free; an allocated one involves a library call that must find and record a block. That difference is real and the model explains it.

Understanding depth limits. Chapter 5.8 asked you to judge recursion depth. The model says why there is a limit at all: each call in progress holds a frame, the region is finite, and nothing reclaims any of it until the calls return.

Reading other people's explanations. Most C writing uses these words freely. You now know what they refer to and, more usefully, which claims they can and cannot support.

Reasoning about what Part II will care about. On an embedded target the sizes of these regions are configured by hand and the consequences of exceeding them are immediate. The model is the right vocabulary for that conversation.

5. What the Model Gets Wrong

The general principle, which this curriculum has applied since Chapter 1.3:

The model tells you what an implementation is likely to do. The standard tells you what you are allowed to rely on. When they disagree, the standard wins, and when you need to know whether a program is correct, the model is not evidence.

6. The Mapping, Collected

Storage durationTypically lives inCreated byReleased by
automatica stack frameentering a blockleaving the block
staticthe static areathe program loadingthe program ending
allocatedthe heapan explicit call — Chapter 9.6an explicit call — Chapter 9.6

The third row is the one that does not exist yet, and reading across it shows why it is different in kind from the other two. Automatic and static objects are created and released by control flow and program startup — things that happen whether or not you think about them. An allocated object is created and released by you, which is the source of both its usefulness and every bug in the second half of this module.

7. What You Should Be Able to Say Now

  1. The stack and the heap are not in the C standard — they are names for what implementations do.
  2. Chapter 9.1's lifetime rule answers whether behaviour is defined; the regions answer why things cost what they cost.
  3. The stack holds a frame per active call — Chapter 5.7's picture, named.
  4. The heap is a managed region that hands out and reclaims blocks in any order.
  5. The static area is prepared before main and is where Chapter 9.3's zero-initialisation happens.
  6. Observed: statics and literals cluster, automatic objects are elsewhere, and their addresses decrease with call depth.
  7. Absolute addresses vary per run; the structure is stable. "Grows downward" is a platform property.
  8. The model is good for cost, depth limits, and reading other people's explanations.
  9. It is wrong about validity, "the heap is slow", "the stack is small", and everything being in memory at all.
  10. Automatic and static objects are managed for you. Allocated objects are managed by you — which is Chapter 9.6's subject.

8. Exercises

Exercise 1 — place each object. For each, say which region it typically lives in and which storage duration it has.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
static int a;
int b;                          /* at file scope */
void f(void) { int c; static int d; }
const char *e = "text";         /* the characters, not the pointer */

Exercise 2 — read the addresses. Given §3's output, say which two objects are in the same region, which direction the stack grew, and which of those two observations is a property of C.

Exercise 3 — classify eight claims. For each, say whether it is an ISO C guarantee, implementation behaviour, or false.

  • a local object's lifetime ends when its block exits
  • a local object is stored on the stack
  • the stack grows toward lower addresses
  • allocating costs more than declaring a local
  • a pointer to a dead local still works if nothing has overwritten it
  • string literals and static objects are in the same area
  • every object exists somewhere in memory
  • recursion depth is limited

Exercise 4 — pick the right tool for the question. For each question, say whether you would answer it with the lifetime rule or with the regions model.

  • is this program's behaviour defined?
  • why did adding a 1 MB local array break the program?
  • can I return this pointer?
  • why is this loop faster after I moved the allocation out of it?

Exercise 5 — explain the register point. Say why an object whose address is never taken might not be in memory at all, what changes the moment you write &x, and what that tells you about the limits of Figure 1.

Exercise 6 — correct the reasoning. A colleague debugs a crash by saying "the pointer still has a sensible value and the data looks right, so the object must still be alive." Give the two-sentence correction and name the chapter whose demonstration proves it.

Exercise 7 — predict the fourth line. Chapter 9.6 will add an allocated object to §3's program. Say whether you expect its address near the statics, near the automatics, or neither, and give your reason from Figure 1.

Exercise 8 — argue the ordering. This curriculum taught lifetime in Chapter 9.1 and the regions here, three chapters later. Give the argument for that order, and one concrete misconception the reverse order tends to produce.

9. Summary

  • The stack and the heap are models, not language. C specifies lifetime, not regions.
  • Use lifetime for correctness; use the regions for cost, depth and reading other people's explanations.
  • Stack — a frame per active call. Heap — managed blocks released in any order. Static area — prepared before main.
  • Measured: statics and literals cluster; automatic addresses decrease with depth; absolute values vary per run.
  • "Grows downward" is a platform property, not a rule.
  • The model is wrong about validity, about the heap being "slow", about the stack's size, and about every object being in memory.
  • Automatic and static are managed for you; allocated is managed by you.

Every object in this module so far has been created by something other than a decision you made at run time — entering a block, or the program starting. That has quietly constrained every example in the curriculum since Module 6: an array's size has always been fixed when the code was written, a buffer has always been sized for the worst case you could imagine, and a function has never been able to produce a result larger than something the caller had already set aside. The next chapter is about the problems those constraints make impossible, because that — and nothing else — is the reason the third storage duration exists.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.