Dynamic Memory Allocation
Allocate memory at runtime! Use malloc, calloc, realloc, and free. Create arrays whose size you don't know until the program runs.
Track Your Progress
Sign in to save your learning progress
What You Will Learn
- ✓Allocate memory with malloc and calloc
- ✓Resize memory with realloc
- ✓Free memory to prevent leaks
- ✓Understand Stack vs Heap allocation
01Static vs Dynamic Memory
C Has Two Types of Memory
The process of reserving memory is called allocation. C has two types: Static memory (compile time) and Dynamic memory (runtime).
Static Memory (Compile Time)
Static memory is reserved before the program runs. C automatically allocates memory for every variable when compiled.
1#include <stdio.h>23int main() {4 // Static allocation: 20 students = 80 bytes5 int students[20];6 printf("Size: %zu bytes\n", sizeof(students));7 8 // Problem: Only 12 students enrolled!9 // 8 slots wasted (32 bytes)10 return 0;11}Size: 80 bytes
Problem with Static Memory
You reserved space for 20 students but only 12 enrolled. 8 slots wasted! And you can't change the array size later.
Understanding the Stack vs Heap
Your program's memory is divided into regions. Understanding this helps you know why we need dynamic memory:
Stack (Automatic)
- • Local variables live here
- • Auto-managed — cleaned up when function ends
- • Fixed size — can't grow at runtime
- • Very fast allocation
Heap (Dynamic)
- • malloc/calloc allocate here
- • YOU manage it — must call free()
- • Can grow — request more anytime
- • Slightly slower but flexible
Analogy: Stack is like a cafeteria tray — fixed size, automatically cleaned. Heap is like a storage unit — rent what you need, but YOU must clean it out!
Dynamic Memory (Runtime)
Dynamic memory is allocated after the program starts running. You have full control over how much memory is used at any time.
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int numStudents = 12; // Actual count6 7 // Allocate exactly what we need!8 int *students = calloc(numStudents, sizeof(int));9 10 printf("Size: %zu bytes\n", numStudents * sizeof(int));11 12 free(students);13 return 0;14}Size: 48 bytes
| Feature | Static Memory | Dynamic Memory |
|---|---|---|
| When allocated | Compile time | Runtime |
| Size | Fixed | Can change (realloc) |
| Memory location | Stack | Heap |
| Access | Variable name | Pointers only |
| Deallocation | Automatic | Manual (free) |
| Example | int arr[20]; | malloc(20*sizeof(int)) |
✓Key Advantages of Dynamic Memory
- 1.Efficient: Allocate exactly what you need, no waste
- 2.Resizable: Grow or shrink arrays with realloc()
- 3.Persistent: Memory survives after function returns
- 4.Large: Heap is much bigger than stack
| Function | Purpose | Header |
|---|---|---|
| malloc() | Allocate memory (uninitialized) | <stdlib.h> |
| calloc() | Allocate + initialize to zero | <stdlib.h> |
| realloc() | Resize existing memory | <stdlib.h> |
| free() | Release memory back to system | <stdlib.h> |
02Stack vs Heap Memory
Understanding Memory Regions
Your program has two main memory areas: Stack (automatic, fast, limited) and Heap (manual, slower, large).
Memory Layout of a C Program:
Local variables, function calls
Dynamic memory (malloc, calloc)
| Feature | Stack | Heap |
|---|---|---|
| Management | Automatic | Manual (you manage) |
| Speed | Very Fast | Slower |
| Size | Limited (~1-8 MB) | Large (GB+) |
| Lifetime | Until function ends | Until you free() |
| Use for | Local variables | Large/dynamic data |
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // STACK: automatic, fixed size6 int a = 10; // On stack7 int arr[5] = {1,2,3,4,5}; // On stack8 9 // HEAP: manual, dynamic size10 int *p = malloc(sizeof(int)); // On heap11 *p = 20;12 13 printf("Stack: a = %d\n", a);14 printf("Heap: *p = %d\n", *p);15 16 free(p); // Must free heap memory!17 return 0;18}Stack: a = 10
Heap: *p = 20
03malloc() - Memory Allocation
malloc() stands for "memory allocation". It allocates a single block of contiguous memory on the heap. The memory is uninitialized (contains garbage values).
Syntax
void* malloc(size_t size);
- • size: Number of bytes to allocate
- • Returns:
void*pointer (needs type casting), orNULLif failed - • Warning: Memory is NOT initialized (contains garbage)
▶ malloc() Visualized Step-by-Step
Your variable
ptr
NULL
HEAP (empty)
int *ptr = malloc(5 * sizeof(int));
ptr
0x1000
HEAP - 20 bytes allocated
Contains garbage values!
ptr[0]=10; ptr[1]=20; ptr[2]=30; ...
ptr
0x1000
✓Now properly initialized!
free(ptr); ptr = NULL;
ptr
NULL
HEAP - Memory returned
Step 1: Basic malloc (Not Recommended)
To store 5 integers, we need 5 × 4 = 20 bytes. This works but is not portable:
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // Hardcoded 20 bytes (5 integers × 4 bytes)6 // Problem: int size varies by system!7 int *ptr = (int *)malloc(20);8 9 for (int i = 0; i < 5; i++)10 ptr[i] = i + 1;11 12 for (int i = 0; i < 5; i++)13 printf("%d ", ptr[i]);14 15 free(ptr);16 return 0;17}1 2 3 4 5
Step 2: Using sizeof() (Better)
The size of int depends on the architecture. Use sizeof() for portable code:
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // sizeof(int) = 4 bytes on most systems6 int *ptr = (int *)malloc(sizeof(int) * 5);7 8 for (int i = 0; i < 5; i++)9 ptr[i] = i + 1;10 11 for (int i = 0; i < 5; i++)12 printf("%d ", ptr[i]);13 14 free(ptr);15 return 0;16}Step 3: Check for NULL (Recommended)
If there's no memory available, malloc returns NULL. Always check!
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = (int *)malloc(sizeof(int) * 5);6 7 // Always check for failure!8 if (ptr == NULL) {9 printf("Allocation Failed!\n");10 return 1;11 }12 13 for (int i = 0; i < 5; i++)14 ptr[i] = i + 1;15 16 for (int i = 0; i < 5; i++)17 printf("%d ", ptr[i]);18 19 free(ptr);20 return 0;21}Type Casting
malloc() returns void*. In C, you can assign it directly to any pointer type. The cast (int *)is optional in C but required in C++.
How malloc() works:
int *ptr = (int *)malloc(sizeof(int) * 5);
ptr points to:
Garbage values! Not initialized
04calloc() - Contiguous Allocation
Syntax
void* calloc(size_t count, size_t size);
- • count: Number of elements
- • size: Size of each element
- • Bonus: Initializes all bytes to ZERO!
| Feature | malloc() | calloc() |
|---|---|---|
| Arguments | malloc(total_bytes) | calloc(count, size) |
| Initialization | Garbage values | All zeros |
| Speed | Slightly faster | Slightly slower |
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = (int *)calloc(5, sizeof(int));6 7 if (ptr == NULL) {8 printf("Allocation Failed!\n");9 return 1;10 }11 12 // No need to initialize - already 0!13 for (int i = 0; i < 5; i++)14 printf("%d ", ptr[i]);15 16 free(ptr);17 return 0;18}0 0 0 0 0
malloc() vs calloc() - Visual Comparison
malloc(5 * sizeof(int))
Garbage values
Must initialize manually!
calloc(5, sizeof(int))
✓All zeros
Ready to use immediately!
How calloc() works:
int *ptr = (int *)calloc(5, sizeof(int));
ptr points to:
All initialized to zero!
When to Use calloc?
Use calloc() when you need zero-initialized memory (like arrays, counters, or structures). Use malloc()when you'll immediately overwrite all values anyway.
05realloc() - Resize Memory
The Resize Problem
You allocated space for 5 students, but now you have 10! realloc() lets you grow or shrink existing memory without losing the original data.
Syntax
void* realloc(void *ptr, size_t new_size);
- • ptr: Pointer to previously allocated memory
- • new_size: New size in bytes
- • Returns: Pointer to resized memory (may be different address!)
Basic realloc Example
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // Start with 5 integers6 int *ptr = (int *)malloc(5 * sizeof(int));7 8 // Resize to hold 10 integers9 ptr = (int *)realloc(ptr, 10 * sizeof(int));10 11 if (ptr == NULL) {12 printf("Reallocation Failed!\n");13 return 1;14 }15 16 printf("Successfully resized to 10 integers\n");17 free(ptr);18 return 0;19}▶ realloc() Visualized - Growing an Array
ptr
0x1000
ptr = realloc(ptr, 8 * sizeof(int));
Need 3 more slots...
ptr
0x2000
(may change!)
✓Old data copied → New slots have garbage values
↘ Shrinking Memory
realloc can also make memory smaller:
realloc(3)→Data beyond new size is lost!
Critical: realloc Can Fail!
If realloc() fails and returns NULL, the original memory is NOT freed. If you overwrite the original pointer, you lose access to that memory = memory leak!
✓Safe realloc Pattern (Recommended)
Use a temp pointer to avoid memory leaks on failure:
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = (int *)malloc(5 * sizeof(int));6 7 // Use temp pointer for safety!8 int *temp = (int *)realloc(ptr, 10 * sizeof(int));9 10 if (temp == NULL) {11 printf("Reallocation Failed!\n");12 // ptr is still valid, can still use or free it13 free(ptr);14 return 1;15 }16 17 // Only update ptr after success18 ptr = temp;19 20 printf("Success!\n");21 free(ptr);22 return 0;23}Grow and Shrink Example
1#include <stdio.h>2#include <stdlib.h>34int main() {5 // Start with 5 integers6 int *ptr = (int *)malloc(5 * sizeof(int));7 for (int i = 0; i < 5; i++)8 ptr[i] = (i + 1) * 10;9 10 // Grow to 8 integers11 ptr = (int *)realloc(ptr, 8 * sizeof(int));12 13 // Shrink back to 5 integers14 ptr = (int *)realloc(ptr, 5 * sizeof(int));15 16 for (int i = 0; i < 5; i++)17 printf("%d ", ptr[i]);18 19 free(ptr);20 return 0;21}10 20 30 40 50
06free() - Release Memory
Critical Rule
Memory allocated using malloc() and calloc()is NOT automatically deallocated. You MUST use free()to release it back to the operating system!
Syntax
void free(void *ptr);
- • ptr: Pointer returned by malloc/calloc/realloc
- • Passing NULL to free() is safe (does nothing)
- • Never free the same pointer twice!
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = (int *)calloc(5, sizeof(int));6 7 // Do some operations...8 for (int i = 0; i < 5; i++)9 printf("%d ", ptr[i]);10 11 // Free memory when done12 free(ptr);13 14 // Good practice: set to NULL15 ptr = NULL;16 17 return 0;18}0 0 0 0 0
How free() works:
Before free(): ptr points to valid memory
After free(): pointer is INVALID (dangling)
After ptr = NULL: Safe, no dangling pointer
Best Practice: Set to NULL After free()
After free(ptr), the pointer still holds the old address (called a dangling pointer). Set it to NULLto prevent accidental use and make debugging easier.
07Accessing Dynamic Memory
Key Insight
Dynamic memory behaves like an array! You can access elements using index notation ptr[i] or pointer dereferencing *ptr.
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int *ptr = calloc(4, sizeof(int));6 7 // Write using array notation8 ptr[0] = 10;9 ptr[1] = 20;10 ptr[2] = 30;11 12 // Write using dereference (same as ptr[0])13 *ptr = 5; // Changes ptr[0] to 514 15 // Read values16 printf("%d %d %d %d\n", ptr[0], ptr[1], ptr[2], ptr[3]);17 18 free(ptr);19 return 0;20}5 20 30 0
| Notation | Meaning | Example |
|---|---|---|
| ptr[0] | First element | ptr[0] = 10; |
| *ptr | First element (same as ptr[0]) | *ptr = 10; |
| ptr[i] | Element at index i | ptr[3] = 40; |
| *(ptr + i) | Same as ptr[i] | *(ptr + 3) = 40; |
sizeof() Doesn't Work on Dynamic Memory!
sizeof(ptr) returns the size of the pointer (8 bytes), NOT the allocated memory size. You must track the size yourself!
08Memory Leaks
What is a Memory Leak?
A memory leak happens when you allocate memory but never free it. The memory remains "occupied" but unreachable. Over time, your program uses more and more memory until it crashes!
▶ Memory Leak Visualized
ptr = malloc(20);ptr
0x1000
ptr = malloc(40); // LEAK!ptr
0x2000
???
Lost!
Over Time - Memory Fills Up
Start
Leaking...
Filling up
CRASH!
Program runs out of memory!
Example: Memory Leak
1#include <stdlib.h>23void leak_example() {4 int *p = malloc(100 * sizeof(int));5 // Oops! Function ends without free()6 // Memory is lost forever!7}89int main() {10 for (int i = 0; i < 1000; i++) {11 leak_example(); // 400 bytes leaked each time!12 }13 // 400 KB leaked in total!14 return 0;15}✓Fixed: No Memory Leak
1#include <stdlib.h>23void no_leak() {4 int *p = malloc(100 * sizeof(int));5 // ... use the memory ...6 free(p); // Always free before returning!7}89int main() {10 for (int i = 0; i < 1000; i++) {11 no_leak(); // Memory freed each time12 }13 // No leak!14 return 0;15}3 Ways to Lose a Pointer (and Leak Memory)
Case 1: Pointer is Overwritten
1int x = 5;2int *ptr;3ptr = calloc(2, sizeof(*ptr));4ptr = &x; // LEAK! Original memory lost!After ptr = &x, the memory from calloc() can no longer be accessed or freed.
Case 2: Pointer Only Exists in Function
1void myFunction() {2 int *ptr = malloc(sizeof(*ptr));3 // Function ends, ptr is gone!4 // Memory still allocated, but unreachable5}67int main() {8 myFunction(); // LEAK!9 return 0;10}Fix: Either free(ptr) before returning, or return the pointer.
Case 3: realloc() Fails and Overwrites Pointer
1int *ptr = malloc(sizeof(*ptr));23// DANGEROUS! If realloc fails, ptr becomes NULL4ptr = realloc(ptr, 2 * sizeof(*ptr));5// Original memory is now unreachable!Fix: Use a temp pointer: temp = realloc(ptr, ...); if(temp) ptr = temp;
✓Best Practices Summary
- 1.Always check for NULL after allocation
- 2.Always free() memory when no longer needed
- 3.Set pointer to NULL after free()
- 4.Use temp pointer with realloc()
09Practical Example: Dynamic Array
1#include <stdio.h>2#include <stdlib.h>34int main() {5 int n;6 printf("How many numbers? ");7 scanf("%d", &n);8 9 // Allocate array based on user input10 int *nums = malloc(n * sizeof(int));11 if (nums == NULL) {12 printf("Allocation failed!\n");13 return 1;14 }15 16 // Read numbers17 printf("Enter %d numbers:\n", n);18 for (int i = 0; i < n; i++) {19 scanf("%d", &nums[i]);20 }21 22 // Calculate sum23 int sum = 0;24 for (int i = 0; i < n; i++) {25 sum += nums[i];26 }27 28 printf("Sum: %d\n", sum);29 30 free(nums);31 return 0;32}How many numbers? 3
Enter 3 numbers:
10 20 30
Sum: 60
10Issues with Dynamic Memory
Dynamic memory is powerful but error-prone. Careful handling is required to avoid high memory usage or system crashes.
| Issue | Description | Prevention |
|---|---|---|
| Memory Leaks | Failing to free memory, exhausting system resources | Always call free() for every malloc/calloc |
| Dangling Pointers | Using pointer after free() causes undefined behavior | Set pointer to NULL after free() |
| Fragmentation | Repeated alloc/dealloc creates unusable memory gaps | Allocate larger blocks, reuse memory |
| Allocation Failures | If malloc fails and returns NULL, program may crash | Always check for NULL before using |
| Double Free | Calling free() twice on same pointer causes crash | Set to NULL after free() |
!Code Pitfalls: Common Mistakes & What to Watch For
These are the most common mistakes that trip up beginners. Study them carefully to avoid hours of debugging!
Not Checking for NULL
int *p = malloc(1000);
*p = 5; // Crash if NULL!
int *p = malloc(1000);
if (p != NULL) *p = 5;
Using Memory After free()
free(p);
*p = 10; // Undefined behavior!
Double free()
free(p);
free(p); // Crash! Double free
Freeing Non-heap Memory
int arr[10];
free(arr); // Crash! Not from malloc
Wrong Size Calculation
int *p = malloc(5);
int *p = malloc(5 * sizeof(int));
Watch Out When Copying Code!
Key Takeaways
- •malloc(size): Allocate bytes (uninitialized)
- •calloc(n, size): Allocate n elements (zeroed)
- •realloc(ptr, size): Resize existing memory
- •free(ptr): Release memory back to system
- •Always: Check for NULL, free when done, set ptr = NULL
int *p = malloc(n * sizeof(int));
if (p == NULL) { return 1; }
free(p);
p = NULL;
Test Your Knowledge
Related Tutorials
Structures and Dynamic Memory
Combine structs with malloc! Create dynamic arrays of structures that grow as needed. Build real data structures.
Pointer Arithmetic in C
Add and subtract with pointers! Learn how pointer math works with arrays and why ptr++ moves by the size of the data type.
C Program Memory Layout
See how C programs are organized in RAM! Learn about Stack, Heap, Data, BSS, and Text segments. Essential for understanding memory.
Have Feedback?
Found something missing or have ideas to improve this tutorial? Let us know on GitHub!