Dynamic Memory
Challenge Gallery
Quick Reference
Allocation functions:
| Function | Use | Initializes? |
|---|---|---|
calloc(count, size) |
Allocate array, zero-filled | Yes (to 0) |
malloc(total_bytes) |
Allocate raw bytes | No (garbage) |
realloc(ptr, new_size) |
Resize existing allocation | No (new bytes are garbage) |
free(ptr) |
Release memory back to OS | N/A |
The lifecycle:
1. Allocate: int *arr = calloc(n, sizeof(int));
2. Check: if (arr == NULL) { /* handle error */ }
3. Use: arr[i] = value;
4. Free: free(arr);
5. NULL out: arr = NULL;
Safe realloc pattern:
int *temp = realloc(arr, new_size * sizeof(int));
if (temp == NULL) {
free(arr); // Clean up original on failure
exit(1);
}
arr = temp; // Only update after confirmed success
Memory layout:
High addresses
┌──────────────────┐
│ Stack │ ← local variables (auto lifetime)
│ ↓ │
│ (free space) │
│ ↑ │
│ Heap │ ← calloc/malloc (manual lifetime)
├──────────────────┤
│ Data / BSS │ ← globals
├──────────────────┤
│ Text │ ← your compiled code
Low addresses
Common Pitfalls
- Forgetting free() — Every calloc/malloc must have a matching free. Valgrind catches these as “definitely lost.”
- Use-after-free — Accessing memory after free() is undefined behavior. Set pointer to NULL after freeing.
- Double free — Calling free() twice on the same pointer corrupts the heap. NULL-check or set to NULL after free.
- Wrong sizeof —
calloc(n, sizeof(int *))allocates pointer-sized slots (8 bytes), not int-sized (4 bytes). Match the type. - Direct realloc assignment —
arr = realloc(arr, size)leaks on failure. Always use a temp variable. - Stack vs heap confusion — Returning a pointer to a local variable returns a dangling pointer. Use calloc for data that must outlive the function.