malloc, calloc, realloc, free
This chapter covers the four dynamic-allocation functions in depth: their contracts, failure modes, and the undefined behavior they can produce if misused.
Why This Matters
Dynamic allocation is how C programs create objects whose size or count is unknown at compile time. Nearly every real data structure uses it, and nearly every memory-safety bug traces back to a violation of these functions' contracts.
Prerequisites
c.memory.1— the memory model.
Core Concept
void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
malloc(size)allocatessizebytes with indeterminate contents.calloc(nmemb, size)allocatesnmemb * sizebytes, zero-initialized.realloc(ptr, size)resizes a previously allocated block; contents up to the
smaller of old/new sizes are preserved. It may move the block.
free(ptr)deallocates a block;free(NULL)is a no-op.
All return NULL on failure (except free). The returned pointer is suitably aligned for any standard type (but not necessarily over-aligned types — see c.mem.alignment).
Syntax
int *p = malloc(sizeof *p);
if (p == NULL) { /* handle */ }
int *arr = calloc(n, sizeof *arr);
void *q = realloc(p, new_size);
free(p);
Examples
Allocation and ownership
#include <stdlib.h>
int main(void)
{
int *p = malloc(sizeof *p);
if (p == NULL) return 1;
*p = 42;
free(p);
return 0;
}
calloc zeroes
#include <stdlib.h>
int main(void)
{
int *a = calloc(10, sizeof *a); /* all elements 0 */
if (a == NULL) return 1;
/* ... */
free(a);
return 0;
}
realloc without leaking on failure
#include <stdlib.h>
int main(void)
{
int *p = malloc(4 * sizeof *p);
if (p == NULL) return 1;
int *q = realloc(p, 8 * sizeof *p);
if (q == NULL) {
/* p is still valid and must still be freed */
free(p);
return 1;
}
p = q; /* now safe to reassign */
free(p);
return 0;
}
The wrong pattern is p = realloc(p, n) directly — if it fails, p is overwritten with NULL and the original block leaks.
How It Works
The allocator maintains free lists and metadata (block size, flags) around the returned pointer. malloc finds a free block of sufficient size (splitting large blocks), free returns a block to the free list and may coalesce with neighbors, and realloc grows in place if possible or allocates a new block and copies. Full internals are in c.memory.5.
Variations
Zero-size allocations
malloc(0) is implementation-defined: it returns either NULL or a unique pointer that may be passed to free. Avoid relying on it.
Overflow in calloc size
calloc(nmemb, size) checks for multiplication overflow; malloc(n * size) does not (the multiplication can overflow before malloc is called). Prefer calloc for counted allocations.
Common Mistakes
p = realloc(p, n)(leaks on failure).- Not checking for NULL.
free(p)then usingp(use-after-free).- Freeing a pointer not from malloc/calloc/realloc.
- Assuming
malloczeroes memory.
Undefined Behavior
- Use-after-free, double free, and freeing an invalid pointer.
VERIFIED - Writing past the allocated size (heap buffer overflow).
VERIFIED - Accessing a block after
reallocmoved it via the stale pointer.
VERIFIED
- Freeing a pointer not returned by the allocator (or already freed).
VERIFIED
Portability
- All four functions are standard.
malloc(0)behavior is implementation-defined.- The returned alignment is sufficient for standard types only.
Under the Hood
On Linux, malloc is typically implemented over brk/mmap: small allocations come from the heap (brk), large ones from mmaped regions. The allocator adds a small header before the returned pointer to track size and status.
Practical Usage
- Always check allocation results.
- Use
sizeof *p(not a type) in the size argument. - Prefer
callocfor zero-initialized counted allocations. - Handle
realloccarefully (use a temporary). - Free exactly once, and consider
p = NULLafterfreeto catch misuse.
Exercises
1. Write a dynamic array that grows with realloc, using the safe temporary pattern. 2. Demonstrate calloc zero-initialization vs. malloc indeterminate contents. 3. Deliberately double-free a pointer and observe ASan's diagnostic. 4. Explain why malloc(n * sizeof(int)) can overflow but calloc(n, sizeof(int)) checks.
Deep Challenge
Implement a small realloc-safe dynamic buffer API with push that grows by a factor (e.g., 2x), handles allocation failure without leaking, and exposes capacity/size. Explain each ownership and overflow decision.
Related Concepts
c.mem.ownership— ownership and lifetime.c.mem.failure— allocation failure.c.mem.alignment— alignment.c.memory.5— allocator internals.
References
- ISO/IEC 9899:2018 §7.22.3 (memory management).
Verification
- All four signatures and contracts.
VERIFIED reallocfailure leaves original block intact.VERIFIEDfree(NULL)is a no-op.VERIFIEDcalloczero-initializes.VERIFIED- No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] malloc
- [ ] calloc
- [ ] realloc (safe pattern)
- [ ] free
- [ ] Allocation failure and NULL checks
- [ ] Overflow in allocation size
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.mem.malloc | 0 | 7 |