Hello,
Great question! I wish that this sort of thing was taught more often, but these days that doesn't seem to be that case.
Typically, an executable (program) has it's [virtual] address space (memory) broken up into several sections, in commonly allocated order, smaller addresses to larger:
- text - the code, and (often) any read-only data goes here.
- bss - statically allocated, uninitialized data.
- heap - dynamically allocated, uninitialized data; usually grows up, towards the stack.
- stack - the call stack; automatic (local) variables, etc.; usually grows down, towards the heap.
malloc() and free() are used to allocate/deallocate memory from the heap section. When a program starts up, part of the program's setup is to initialize data structures used by malloc() to 'break up' the heap into smaller, more manageable pieces. The details of how malloc() does its work are implementation specific (sorry), but generally, the goals are:
- make allocating a new (or deallocating an old) block of memory fast,
- with as little waste as necessary,
- while avoiding fragmenting the heap too much (which can make later allocations more difficult), and
- while being able to recognize and/or avoid certain errors (out of memory, double-free()'s, corruption, etc.).
I hope this answers your question. If not, perhaps I can provide more detail, later.