C — Memory Management
Stack vs heap
| Feature | Stack | Heap |
|---|---|---|
| Allocation | Automatic | Manual (malloc/free) |
| Speed | Fast | Slower |
| Size | Limited (usually 1-8 MB) | Limited by RAM |
| Lifetime | Scope-based | Until free() |
malloc
Allocate a block of uninitialized memory:
#include <stdio.h>
#include <stdlib.h>
int main() {
// Allocate memory for one int
int *p = malloc(sizeof(int));
if (p == NULL) {
fprintf(stderr, "malloc failed\n");
return 1;
}
*p = 42;
printf("Value: %d\n", *p);
free(p);
// Allocate array
int n = 5;
int *arr = malloc(n * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "malloc failed\n");
return 1;
}
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n"); // 0 10 20 30 40
free(arr);
return 0;
}
calloc
Allocate and zero-initialize memory:
#include <stdio.h>
#include <stdlib.h>
int main() {
// calloc: allocates AND zeros the memory
int *arr = calloc(5, sizeof(int));
if (arr == NULL) {
fprintf(stderr, "calloc failed\n");
return 1;
}
// All elements are 0
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n"); // 0 0 0 0 0
free(arr);
return 0;
}
Key difference: malloc leaves memory uninitialized (garbage values), calloc sets everything to zero.
realloc
Resize a previously allocated block:
#include <stdio.h>
#include <stdlib.h>
int main() {
int capacity = 2;
int size = 0;
int *arr = malloc(capacity * sizeof(int));
if (arr == NULL) return 1;
// Simulate dynamic growth
for (int i = 0; i < 10; i++) {
if (size >= capacity) {
capacity *= 2;
int *temp = realloc(arr, capacity * sizeof(int));
if (temp == NULL) {
fprintf(stderr, "realloc failed\n");
free(arr);
return 1;
}
arr = temp;
printf("Resized to %d\n", capacity);
}
arr[size++] = i;
}
printf("Array: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
return 0;
}
free
Release allocated memory:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(sizeof(int));
*p = 42;
free(p);
p = NULL; // Good practice: prevent dangling pointer
// Don't use p after free!
// printf("%d\n", *p); // Undefined behavior
// Don't double-free!
// free(p); // Undefined behavior
return 0;
}
Common memory errors
#include <stdio.h>
#include <stdlib.h>
int main() {
// MEMORY LEAK: forgetting to free
int *leak = malloc(sizeof(int));
*leak = 42;
// leak is never freed — memory is leaked
// Fix: free(leak);
// DANGLING POINTER: using after free
int *ptr = malloc(sizeof(int));
*ptr = 100;
free(ptr);
// *ptr = 200; // Undefined behavior!
ptr = NULL; // Safe: prevents accidental use
// DOUBLE FREE: freeing twice
int *dup = malloc(sizeof(int));
free(dup);
// free(dup); // Undefined behavior!
dup = NULL;
// BUFFER OVERFLOW: writing beyond allocated size
int *small = malloc(3 * sizeof(int));
// small[5] = 99; // Undefined behavior!
free(small);
printf("Memory management patterns demonstrated\n");
return 0;
}
Dynamic 2D arrays
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
// Allocate rows
int **matrix = malloc(rows * sizeof(int *));
if (matrix == NULL) return 1;
// Allocate each row
for (int i = 0; i < rows; i++) {
matrix[i] = malloc(cols * sizeof(int));
if (matrix[i] == NULL) return 1;
}
// Fill
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j;
}
}
// Print
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%3d", matrix[i][j]);
}
printf("\n");
}
// Free
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Memory debugging tools
| Tool | Purpose |
|---|---|
valgrind | Detect memory leaks and errors |
| AddressSanitizer | Compile-time memory error detection |
-Wall -Wextra | Compiler warnings |
# Compile with AddressSanitizer
gcc -fsanitize=address -g program.c -o program
# Run with valgrind
valgrind --leak-check=full ./program
Best practices
- Always check if
malloc/calloc/reallocreturnedNULL - Always
freewhat youmalloc - Set pointers to
NULLafter freeing - Never use memory after freeing it
- Match allocation and deallocation functions
- Use
sizeoffor portability, not hardcoded sizes
Mini Practice
Write C code that:
- Dynamically allocates an array of 10 integers
- Uses
reallocto grow it to 20 elements - Creates a dynamic 2D array and frees it properly
- Demonstrates a memory leak and how to fix it
Up Next
Congratulations! You've completed the C fundamentals. Continue exploring advanced topics like data structures, algorithms, and system programming.
Related Topics
Frequently Asked Questions about Memory Management
What is Memory Management in C?
Memory Management is a fundamental concept in C. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Memory Management?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Memory Management.
Why is Memory Management important in C?
Memory Management is essential for C development. Understanding this concept will help you write better code and solve real-world problems more effectively.