Chapter 12: Dynamic Memory Allocation
This is one of the most powerful features of C. Until now, we declared arrays and variables with fixed size at compile time (like int arr[100];). But what if we don’t know how many elements we need until the program is running? That’s where dynamic memory allocation comes in – we allocate memory while the program is running.
C gives us four important functions for this:
- malloc()
- calloc()
- realloc()
- free()
All these functions are declared in <stdlib.h> header.
1. Why Do We Need Dynamic Memory Allocation?
Fixed-size arrays have problems:
- Size must be known at compile time (you can’t say int arr[user_input]; in old C)
- If you declare int arr[10000]; but only need 10 elements → wastes memory
- If you need 1 million elements but declared only 1000 → program crashes (array overflow)
Dynamic memory solves these problems:
- Allocate exactly the memory you need at runtime
- Resize memory when needed
- Free memory when you don’t need it anymore → good for memory efficiency
2. malloc() – Memory Allocation (Most Common)
Syntax:
|
0 1 2 3 4 5 6 |
void *malloc(size_t size); |
- size = number of bytes you want
- Returns a void* pointer to the allocated memory (or NULL if failed)
- Memory is not initialized (contains garbage values)
Example: Allocate array of 5 integers dynamically
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <stdio.h> #include <stdlib.h> int main() { int *arr; int n = 5; int i; // Allocate memory for 5 integers arr = (int *)malloc(n * sizeof(int)); // Always check if allocation succeeded if (arr == NULL) { printf("Memory allocation failed!\n"); return 1; } // Take input printf("Enter %d numbers:\n", n); for (i = 0; i < n; i++) { scanf("%d", &arr[i]); } // Print them printf("You entered: "); for (i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); // Very important – free the memory! free(arr); return 0; } |
Note:
- sizeof(int) = usually 4 bytes
- n * sizeof(int) = total bytes needed
- Always cast the void* to correct type (though in modern C it’s optional)
3. calloc() – Contiguous Allocation
Similar to malloc(), but initializes all bytes to 0
Syntax:
|
0 1 2 3 4 5 6 |
void *calloc(size_t num_elements, size_t element_size); |
Example:
|
0 1 2 3 4 5 6 |
int *arr = (int *)calloc(5, sizeof(int)); // Allocates 5 integers, all set to 0 |
Difference from malloc():
- malloc(5 * sizeof(int)) → garbage values
- calloc(5, sizeof(int)) → all zeros (very useful for arrays and structures)
4. realloc() – Re-allocate (Resize) Memory
Used to change the size of previously allocated memory.
Syntax:
|
0 1 2 3 4 5 6 |
void *realloc(void *ptr, size_t new_size); |
- ptr = old pointer (from malloc/calloc)
- new_size = new total bytes
- Returns new pointer (may be different from old one!)
Example: Increase array size from 5 to 10
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <stdio.h> #include <stdlib.h> int main() { int *arr = (int *)malloc(5 * sizeof(int)); int i; // Fill first 5 for (i = 0; i < 5; i++) { arr[i] = i + 1; } printf("Before realloc: "); for (i = 0; i < 5; i++) printf("%d ", arr[i]); printf("\n"); // Resize to 10 elements arr = (int *)realloc(arr, 10 * sizeof(int)); if (arr == NULL) { printf("Reallocation failed!\n"); return 1; } // Fill new 5 elements for (i = 5; i < 10; i++) { arr[i] = i + 1; } printf("After realloc: "); for (i = 0; i < 10; i++) printf("%d ", arr[i]); printf("\n"); free(arr); return 0; } |
Output:
|
0 1 2 3 4 5 6 7 |
Before realloc: 1 2 3 4 5 After realloc: 1 2 3 4 5 6 7 8 9 10 |
5. free() – Release Memory
Very important! Always free memory when you don’t need it anymore.
|
0 1 2 3 4 5 6 |
free(ptr); |
- After free(ptr), don’t use ptr again (it becomes dangling pointer)
- Set ptr = NULL; after free → good practice
6. Memory Leaks and Best Practices
Memory Leak = Allocating memory but forgetting to free it → Program keeps using more and more memory → slow or crash
Best Practices:
- Always check if allocation succeeded
C0123456if (ptr == NULL) { printf("Memory allocation failed!\n"); exit(1); }
- Always free() what you malloc()/calloc()/realloc()
- Don’t free() the same pointer twice → crash
- Don’t use pointer after free() → undefined behavior
- After free(), set pointer to NULL
C01234567free(ptr);ptr = NULL;
- Use one free() per allocation
- Be careful with realloc() – if it fails, old memory is still valid
Example of Memory Leak (Bad Code):
|
0 1 2 3 4 5 6 7 8 9 10 |
void badFunction() { int *p = (int *)malloc(100 * sizeof(int)); // Do something... // Forgot to free(p) → memory leak! } |
Correct Code:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void goodFunction() { int *p = (int *)malloc(100 * sizeof(int)); if (p == NULL) return; // Use p... free(p); p = NULL; } |
Today’s Homework
- Write a program that asks the user how many numbers they want to store → allocate array dynamically → take input → print sum and average.
- Create a dynamic array that grows automatically (use realloc) when user wants to add more numbers.
- Write a program that allocates memory for 5 strings using dynamic allocation (use char *str = malloc(100);).
- Intentionally create a memory leak and think how you would fix it.
