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 automatic memory Stack memory β allocated and freed implicitly by the compiler at function call/return; don't keep long-lived data there. defined in ch. 14 β open in glossary β 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:
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.
14.2 The malloc() Call
malloc() malloc The C library call requesting heap bytes (size via sizeof()); returns a pointer, or NULL on failure. A library call, not a system call.
defined in ch. 14 β open in glossary
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() free Releases a malloc'd region β pointer only, no size (the library tracks it). Knowing when/how/if to call it is the hard part. defined in ch. 14 β open in glossary 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?