Proven C Book←↑→

5 The regions of memory — where a program puts what

What to know first

chapter 3, Words and addresses · the numbers stamped on memory’s cells
chapter 4, Special knowledge about addresses · the odd corners of the address space

Looking back

Chapters 3–4 drew memory as a corridor of numbered cells. The corridor runs in one line — so where along it do a program’s instructions, and the values those instructions handle, actually sit? Are they simply mixed together?

A. Both are in memory — but they are not mixed together in the same place. A program’s memory is divided into regions of different character, and each region has different rules. Some regions stay as they are until the program ends, some live only while one function runs, and some the programmer borrows and gives back personally. Draw this rough sketch first and everything later finds its place.

The need for this chapter, and its context

This chapter comes right after addresses. chapters 3–4 drew memory as a corridor of numbered cells in a row. This chapter divides that corridor into regions by use — one corridor, but one stretch holds instructions and another holds values kept for a moment.

Memory layout comes before syntax for a further reason. Reverse the order and nearly every word soon to arrive — local variable, global, malloc, stack overflow — becomes a term to be memorised without knowing where the thing sits. Fix the names and characteristics of the regions first and those words arrive already having a place. That is why a chapter with not one line of C syntax closes Part I.

By the end of this chapter

We first learn that the memory a program uses is not one lump but is divided into a few regions of different purpose. Code, the fixed values (the static region), the workbench (the stack), the warehouse (the heap) — learn only the names and characteristics first and nearly every later chapter builds on this sketch. Not one piece of C syntax appears yet.

The questions this chapter answers

  1. Why divide it at all? Is memory not just one lump?
  2. Then where does the rule “an uninitialised value is 0” come from?
  3. Then is the sketch this chapter taught untrustworthy?
regions

Figure 5.1 — The memory layout of one program. Nearly every later chapter builds on this picture.

5.1 Four regions, one metaphor#

Think of a person working at a desk and the picture falls into place.

regionmetaphorwhat is placed therehow long it lives
codethe work orderthe program’s instructionsthe whole program (usually read-only)
the static regionbuilt-in shelvesvalues the whole program sharesthe whole program
the stackthe workbenchtemporary values needed by the work at handuntil that work ends
the heapthe warehousevalues whose size and lifetime you settle yourselfuntil it is given back

Table 5.1 — The four regions of memory and what each is like

Code is only read. Rewriting the work order during execution is usually forbidden.

The static region is set up when the program starts and stays as it is until it ends. So a value there can be seen, read and changed from anywhere in the program, at any time. That is convenient. But because anything can change it, when the value goes wrong it is hard to find where it was changed.

The stack is the workbench. Begin a task and you spread out on the bench what it needs; when the task ends you clear it all away. It is fast and automatic but narrow — and once cleared away, the things that were there cannot be found again.

The heap is the warehouse. Ask “lend me this much” and it gives you room, and when you have finished you must give it back. The size can be decided during execution and it can be kept alive as long as you wish; in exchange the responsibility of returning what was borrowed lies with the programmer.

Q. Why divide it at all? Is memory not just one lump?

A. Physically it is — the one corridor of lockers we saw in chapter 3. The division is an agreement, and that agreement gives two things.

First, automatic management. Gather the temporary values one function uses in one region (the stack) and they can all be cleared away when that function ends. There is no need to look after them one by one.

Second, protection. Making the code region read-only can prevent the program from changing its own instructions by mistake, or an attacker from changing them on purpose. Giving different permissions per region is the basic defence of modern operating systems.

5.2 Why the static region divides in two — the strange name bss#

In practice the static region is split in two again. The name is unfamiliar but the story is simple.

The second region’s name is bss, short for Block Started by Symbol. It came from a pseudo-operation in the mid-1950s assembler for the IBM 704 (UA-SAP), whose job was “attach a symbol and reserve this many words of uninitialised space after it”.1 The meaning was forgotten as the name crossed more than half a century — yet spelled out, it still describes exactly what the region does. Thanks to this distinction, the executable of a program that “starts a table of a million slots all at zero” does not grow large.

Q. Then where does the rule “an uninitialised value is 0” come from?

A. It holds for the static region only — and that 0 is not free but something somebody filled in. The operating system gives a place already filled with zeros, or on a machine with no operating system the program’s startup code runs a loop itself and fills it with zeros.

Conversely, a temporary value on the workbench (the stack) is not guaranteed to be 0. It inherits as it stands the place some other work just used and left, so what remains there cannot be known. This difference is one of the places people learning C stumble at most often, and chapter 45 treats it formally.

5.3 Static, stack and heap: the three regions contrasted#

A table we shall keep using. For now it is enough to get the feel.

staticstackheap
when the size is decidedat translationat translation (usually)during execution
lifetimethe whole programuntil that work endsuntil it is given back
tidying upautomaticautomaticby the programmer
speedfastfastestrelatively slow
room to sparefixednarrow (usually a few MiB)wide
common accidentschanged from anywhereoverflow, referring to a dead placeleaks, giving back twice

Table 5.2 — The axes that separate static, stack and heap

The last row of this table takes up a good deal of the latter part of this book. C’s reputation as powerful and dangerous comes mostly from what happens when the rules of these three regions are broken.

In practice. A sense of how narrow the workbench is

Even on today’s computers, whose warehouse (the heap) is tens of gigabytes, the workbench (the stack) is narrow. Common defaults are 8 MiB on Linux and 1 MiB on Windows — one part in several thousand of the whole memory.

So a beginner’s first collapse mostly happens here. Try to spread a big table whole on the workbench, or write a program that calls itself endlessly, and the workbench overflows and the program dies on the spot — the name is exactly that, stack overflow (the name of the world’s most famous programming question site came from here). The detailed numbers and remedies are treated in chapters 45 and 98.

A common misconception. “Using a lot of memory makes a program slow”

Half right. The problem is not the amount but which region is used and how. Values used briefly on the workbench and cleared away are nearly free however many of them there are, while repeatedly borrowing from and returning to the warehouse is noticeably slow even with small amounts (we measure it in chapter 46). And for a reason we shall see in chapter 12, the same amount scattered or gathered makes a severalfold difference. A sense for handling memory grows not from “how much” but from “where, and in what order”.

5.4 How far this sketch holds#

The four regions drawn so far really do look like that on the machines in wide use today. The desktops and servers running Linux, Windows and macOS, and the overwhelming majority of programs on them, have this shape. So carry the sketch with you.

But one more thing must be understood alongside it. ★ This four-region shape is not the shape the C language demands.

5.4.1 The standard has no word “stack”#

Read the C23 standard document from beginning to end and the word stack never appears, not once. Nor does heap.2 This is no surprise. The standard does not fix machinery; it fixes properties that must hold.

What this chapter saidWhat the standard actually says
it is placed on the stackautomatic storage duration — created on entering a block, gone on leaving it
it lives in the static regionstatic storage duration — it lives as long as the program
it is borrowed from the heapallocated storage duration — what the malloc family gives
stack frames pile uprecursive calls are permitted

Table 5.3 — This chapter’s words and the standard’s words

The last row matters most. Instead of demanding a stack, the standard writes this.

“ Recursive function calls shall be permitted, both directly and indirectly through any chain of other functions.3 ”

★ The difference between demanding a stack and demanding recursion is decisive. Not “provide a stack” but “make recursion work.” A stack is merely the most common way to satisfy that demand, not the only one. The standard leaves the method to the implementation and demands only the result — the idea of the abstract machine from chapters 14–15 is at work here too.

5.4.2 Machines that really are different — today#

Not “could differ in theory”: things being sold right now. The 8-bit PIC microcontroller is a good example.

In practice. A machine with a stack you cannot use

Microchip’s 8-bit PIC family has a hardware stack. Its character is quite unlike the workbench we drew.

  • Its depth is fixed. Eight levels on the mid-range core, sixteen on the enhanced mid-range. Nest calls deeper than that and it overflows — it cannot be enlarged.
  • ★ You cannot put values on it. All that stack holds is return addresses, and the program has no means to push onto it or read from it. It is a different thing entirely from data memory.

So where do local variables go? The compiler analyses the call graph in advance and assigns them places. Microchip’s XC8 compiler calls this the compiled stack, and its manual states that it “is statically allocated”, and that for devices which do not support the reentrant model, “all functions are encoded to use the compiled stack, which are non-reentrant functions.”4

★ Here this limitation collides head-on with the standard clause above. A non-reentrant function cannot call itself. The standard said recursion shall be permitted, and in this arrangement it does not come free. So a compiler for such an environment either builds a separate software stack when recursion is needed (slow, and it eats RAM) or tells the programmer not to recurse. The same manual notes the reason: since the number of iterations of a recursive call cannot be predicted, “the number of hardware stack levels and the total software stack size cannot be determined, so no stack guidance is possible.”

This is the reality of the freestanding environment of chapter 109 — what happens when the hardware does not provide for free what the standard demands.

Historically, such machines were the common case.

Machine or eraWhat was different
early mainframes (1950s–60s)the very idea of a call stack was not standard. A fixed slot holding the return address per function was common — and that makes recursion impossible in principle
early Fortran and COBOLlocal variables were allocated statically, so recursion was not supported
Burroughs B5000 (1961)the opposite extreme — the stack was in the hardware, and the instruction set itself was a stack machine
Harvard architecturecode and data live in different memories altogether. “One address space divided into four” does not hold
today’s 8-bit MCUsexactly as in the box above

Table 5.4 — Machines where this sketch does not hold

Q. Then is the sketch this chapter taught untrustworthy?

A. No. Separate two things.

① “What it looks like” is usually right. In the environments this book addresses — desktops, servers, phones, and most embedded systems that have an operating system — the four-region picture is a fact. Open a debugger and it really looks like that.

② “Must it be so” is a different question. Which is why you must not write code that leans on this sketch. Assuming the stack grows downwards, assuming local variables are neighbours in memory, comparing the addresses of two locals to judge their order — none of that is guaranteed by the standard, and machines really do differ (chapter 38). The layout extended to embedded systems is in chapter 98.

★ In short: use the sketch to understand, and write code against the contract. That distinction runs through the whole book. This chapter drew a picture for understanding; Table 5.3 marks which parts of that picture are promises.

5.5 Where this sketch will be used#

For now it is enough to know the names. These four regions keep returning through the whole book.

wherewhat
chapter 12the ladder of memory — even the same region differs in speed by cache
chapter 45lifetime and storage duration — what C’s grammar calls these regions
chapter 46dynamic memory — the discipline of borrowing from and returning to the warehouse
chapter 98the real layout in operating systems and embedded systems
chapter 99inside the allocator that manages the warehouse

Table 5.5 — Where the four regions come back

We have seen what a program puts where. That is the end of Part I. It covered what the machine and its memory look like, which is worth knowing before C itself, and not one line of syntax appeared.

Part II moves on to what goes into those lockers, and how: integers, numbers with a decimal point, characters, and streams of characters. Climb that ladder of representation, from integers to streams of characters, and we then see what “being executed” actually involves (chapter 10), and how bold a lie this simple picture of the machine from Part I has been all along (chapters 12–15).

Notes

  1. UA-SAP (United Aircraft Symbolic Assembly Program), for the IBM 704 in the mid-1950s. The keyword carried over into FAP, the standard assembler for IBM’s 709 and 7090 machines. ↩
  2. ISO/IEC 9899:2024 is the number of the C standard published jointly by the International Organization for Standardization (ISO) and the International Electrotechnical Commission (IEC). A mechanical search of all 759 pages of N3220, its widely referenced working draft, found stack 0 times and heap 0 times. How to obtain the standard is in appendix P. ↩
  3. ISO/IEC 9899:2024 §6.5.3.3 p9 (Function calls). ↩
  4. MPLAB XC8 C Compiler User’s Guide for PIC MCU (Microchip, DS50002737). ↩