P-3.4 Dynamic Memory, and Leaks

Standard C allocation semantics and memory-checking tools — written September 2026

What this is and why it exists

Sometimes the size or the lifetime of something is not known while compiling. Then you ask for memory while the program runs, and you become responsible for giving it back.

That responsibility is the subject here. It is also the origin of nearly every ownership convention in every later language. Garbage collection, reference counting, borrow checking: all of them are attempts to automate this by hand-work you are about to do once.

Do it once, and watch a tool point at the exact line where a leak began. Those later mechanisms then feel like answers to a problem you have actually had.

The vocabulary

  • Allocation — asking for a block of memory while the program runs.
  • Release — giving that block back.
  • Ownership — which part of the code is responsible for releasing a block.
  • Leak — storage that is never given back.
  • Double free — releasing the same block twice.
  • Use-after-free — reading or writing a block after releasing it.
  • Memory checker — a tool that watches allocations and reports all three.

The mental model

An allocation returns an address, or it returns nothing at all. Code that assumes success will follow a null pointer on the day memory runs short. Which is, of course, the worst day. The check costs one line and it is not optional.

Every allocation needs exactly one matching release. "Exactly one" means somebody has to decide, for each piece of storage, which part of the code is responsible for it. That decision is ownership, and it is far better written down than assumed. Most memory defects in real programs are two pieces of code each believing the other one owns something.

A leak is storage never given back. Here is why leaks are underestimated. In a program that starts, does its job and exits, a leak does no visible harm. The operating system reclaims everything on exit. The same leak in something long-running is fatal. This is why servers fall over after four days rather than immediately. It is also why the defect is found a long way from its cause.

Double free and use-after-free are the other two. Releasing a block twice, or touching it after release, corrupts the bookkeeping the allocator itself depends on. The consequence is that the crash usually happens somewhere else entirely, in unrelated code, at an unrelated time. That displacement is what makes them so unpleasant to chase by reading.

So do not chase them by reading. Run a memory checker over a small program you wrote yourself. It will name the line where leaked storage was requested and the line where invalid access happened. Do this on your own code, where you already know the answer. That is the fastest way to learn to trust the tool when you do not.

What you should now be able to explain or do

Allocate memory, check that you got it, and release it exactly once. State who owns each block, in writing. Explain why a leak is harmless in a short program and fatal in a long-running one. Say what double free and use-after-free corrupt, and why the crash appears elsewhere. Run a memory checker over your own program and read its report.

Check yourself

It can return nothing when memory is short. Code that assumes success follows a null pointer exactly when the machine is already in trouble.

Which part of the code must release a block. Most memory defects are two pieces of code each assuming the other one owns it.

A short program exits and the operating system reclaims everything. Only something long-running accumulates enough to fail.

It corrupts the allocator's own bookkeeping. The damage is discovered later, by whichever allocation happens to touch the corrupted structure.

Because you can check the tool against a known answer. That is what makes its report trustworthy on code you do not understand.

Go deeper

We haven't checked most of these for screen reader use yet.

Back to Dynamic Memory, and Leaks: work through the checklist