Β§14.1–14.3Types of Memory … The free() Call

Part I OSTEP pp. 131–134 Β· ~5 min read

  • automatic memory
  • malloc
  • free

Second interlude β€” short and pointy, as the book promises all chapters are (this one more than most).

The Crux: How To Allocate And Manage Memory

In UNIX/C programs, understanding how to allocate and manage memory is critical in building robust and reliable software. What interfaces are commonly used? What mistakes should be avoided?

14.1 Types of Memory

A running C program has two kinds of memory. Stack memory β€” a.k.a. automatic memory β€” is managed implicitly by the compiler:

void func() {
  int x; // declares an integer on the stack
  ...
}

Space appears when func() is called and vanishes when it returns β€” so information that must outlive the call had better not live there. That need is what the heap is for, where allocation and deallocation are handled explicitly, by you. A heavy responsibility, and the cause of many bugs β€” the next page’s whole gallery of them. Here’s the heap version, and it’s sneakier than it looks:

void func() {
  int *x = (int *) malloc(sizeof(int));
  ...
}

That single line allocates memory twice β€” step through it:

One line, two memories: int *x = (int *) malloc(sizeof(int));
stack (automatic) func()'s frame x: (pointer, garbage) heap (yours to manage) (nothing allocated yet)

1Call func()

The compiler pushes a stack frame automatically. Seeing the declaration int *x, it reserves a slot in the frame for the pointer β€” which currently holds garbage. This is stack (automatic) memory: you did nothing, and you will do nothing to release it either.

step 1 / 4

14.2 The malloc() Call

malloc() is simple: pass a size, receive a pointer to fresh heap space β€” or NULL on failure. man malloc tells you everything:

#include <stdlib.h>
...
void *malloc(size_t size);

(You don’t strictly need the header β€” libc has the code regardless β€” but including it lets the compiler check you’re calling it correctly.) Nobody types raw byte counts; use sizeof():

double *d = (double *) malloc(sizeof(double));

That’s a lot of double-ing. Note that sizeof() is a compile-time operator, not a function β€” the 8 is substituted before the program ever runs. And it has a famous trap:

int *x = malloc(10 * sizeof(int));   // an array of 10 ints β€” fine
printf("%d\n", sizeof(x));           // prints 4 or 8. NOT 40!

int y[10];
printf("%d\n", sizeof(y));           // prints 40 β€” enough static info

sizeof(x) asks how big a pointer is; the compiler has no idea what that pointer refers to. Strings have their own idiom β€” malloc(strlen(s) + 1) β€” the +1 for the end-of-string character (sizeof may mislead you here too). And the (int *) cast? Pure reassurance for compiler and reader β€” β€œyeah, I know what I’m doing” β€” not needed for correctness.

Tip: When in doubt, try it out

Not sure how a routine or operator behaves? No substitute for writing a little code and testing it. Man pages are useful; how it behaves in practice is what matters. (This is exactly how the book double-checked its own claims about sizeof().)

14.3 The free() Call

Allocation, it turns out, is the easy half. To release heap memory:

int *x = malloc(10 * sizeof(int));
...
free(x);

free() takes just the pointer β€” no size. The allocation library tracks how big each region is (a detail with real consequences when chapter 17 builds such a library). Knowing when, how, and even if to free β€” that’s the hard part, and the source of the bug gallery that awaits in the next section.

Check yourself

1.Who manages stack memory, and who manages heap memory?

2.How many allocations happen in: int *x = (int *) malloc(sizeof(int)); ?

3.Predict: int *x = malloc(10 * sizeof(int)); printf("%d", sizeof(x)); prints…

4.Why is the string idiom malloc(strlen(s) + 1), not malloc(strlen(s))?

5.free(x) takes no size argument. Who knows how big the region is?

5 questions