Dynamic Memory in C
Stack memory is automatic and limited. Heap memory via malloc/free gives you control over lifetime and size at the cost of manual management.
malloc and free
#include <stdlib.h>
int *arr = malloc(10 * sizeof *arr);
if (arr == NULL) { /* handle OOM */ }
arr[0] = 42;
free(arr);
arr = NULL; /* prevent dangling pointer */
calloc and realloc
int *zeros = calloc(100, sizeof *zeros); /* zero-initialized */
int *grown = realloc(arr, 20 * sizeof *arr);
if (grown == NULL) {
/* arr still valid — don't lose it */
} else {
arr = grown;
}
Ownership Patterns
typedef struct {
size_t len;
char *data;
} String;
void string_free(String *s) {
free(s->data);
s->data = NULL;
s->len = 0;
}
Common Bugs
/* Double free — undefined behavior */
free(p);
free(p);
/* Use after free */
free(p);
*p = 1;
/* Memory leak — forgot free */
int *p = malloc(100);
/* p goes out of scope without free */
Debugging Tools
# AddressSanitizer
gcc -fsanitize=address -g prog.c -o prog
# Valgrind (Linux/macOS)
valgrind --leak-check=full ./prog
Common Pitfalls
- Treating compiler warnings as optional rather than actionable feedback.
- Skipping error checks on library and system calls.
- Copy-pasting examples without adapting to your project’s conventions.
Best Practices
- Enable strict compiler warnings and fix them before merging.
- Write small, testable units with clear input/output contracts.
- Document non-obvious invariants and preconditions.
- Use version control and code review for every change.
Memory and Performance Notes
malloc has overhead per allocation (metadata). Pool allocators and arena allocators reduce fragmentation in long-running servers.
Exercise
Implement a dynamic array that doubles capacity when full. Include push, pop, and destroy functions.
Hint: On realloc failure, keep the old pointer. On destroy, free inner pointers before the struct.
Summary
Apply these concepts in small programs before moving to larger projects. Combine with adjacent topics in the learning path for deeper mastery.
Real-World Application
These concepts appear in production codebases — from operating system kernels to embedded firmware. Study open-source projects that use this topic extensively to see idiomatic patterns at scale.
Debugging Checklist
- Reproduce the issue with the smallest possible input.
- Enable compiler warnings and sanitizers.
- Use a debugger to inspect state at the failure point.
- Verify assumptions about types, sizes, and return values.
- Compare working and broken code paths side by side.
- Write a regression test once the bug is fixed.
Further Reading
Consult the ISO C standard, Effective C by Robert C. Seacord, and your compiler documentation for platform-specific behavior.
Quick Reference
Review the code examples on this page before starting the exercise. Type them manually to build muscle memory.
Additional Examples
Consider how this topic applies in a larger project:
// Break the problem into smaller functions
// Test each function independently
// Integrate incrementally
Working through variations of the examples above builds deeper understanding than reading alone.