Dynamic Memory Allocation #
What is Dynamic Memory Allocation? #
Dynamic memory allocation allows programs to request memory at runtime rather than compile time.
Used when:
- The amount of data is not known beforehand.
- Data structures need to grow or shrink during execution.
Why Not Static Allocation? #
Static Memory | Dynamic Memory |
---|---|
Size fixed at compile time | Size determined at runtime |
Allocated on stack | Allocated on heap |
Automatically freed | Must be freed manually |
Limited size | Flexible and scalable |
Functions in <stdlib.h>
#
Function | Description |
---|---|
malloc(size) | Allocates uninitialized memory |
calloc(n, size) | Allocates and zero-initializes memory |
realloc(ptr, size) | Changes the size of an existing allocation |
free(ptr) | Frees allocated memory |
🧩 Example 1: Using malloc()
#
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Enter number of integers: ");
scanf("%d", &n);
int *arr = malloc(n * sizeof(int));
if (!arr) {
printf("Memory allocation failed!\n");
return 1;
}
for (int i = 0; i < n; i++) arr[i] = i * i;
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
free(arr);
return 0;
}
✅ Allocates memory for n
integers on the heap.
🧩 Example 2: calloc()
for Zero Initialization
#
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = calloc(5, sizeof(int));
for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
free(arr);
}
✅ calloc()
ensures all elements are initialized to 0.
🧩 Example 3: realloc()
for Resizing
#
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = malloc(3 * sizeof(int));
for (int i = 0; i < 3; i++) arr[i] = i + 1;
arr = realloc(arr, 5 * sizeof(int));
arr[3] = 4; arr[4] = 5;
for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
free(arr);
}
✅ Expands or shrinks a previously allocated block.
🔹 Common Use Case 1: Dynamic Arrays #
When user input size is unknown at compile time.
int *arr = malloc(n * sizeof(int));
🔹 Common Use Case 2: Social Networks #
Different people can have different number of friends. So we need something like a 2d array with different rows having different sizes.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Person {
char name[10];
// friends is an array of person pointers
struct Person** friends;
int num_friends;
} Person;
int main() {
Person* a = malloc(sizeof(Person));
strcpy(a->name, "Alice");
Person* b = malloc(sizeof(Person));
strcpy(b->name, "Bob");
Person* c = malloc(sizeof(Person));
strcpy(c->name, "Charlie");
Person* d = malloc(sizeof(Person));
strcpy(c->name, "Diestel");
// a has 2 friends: b, c
a->friends = malloc(2*sizeof(Person*));
a->friends[0] = b;
a->friends[1] = c;
a->num_friends = 2;
// b has 2 friends: c, a
b->friends = malloc(2*sizeof(Person*));
b->friends[0] = c;
b->friends[1] = a;
b->num_friends = 2;
// c has 1 friend: d
c->friends = malloc(1*sizeof(Person*));
c->friends[0] = d;
c->num_friends = 1;
// d has no friends
d->friends = NULL;
d->num_friends = 0;
return 0;
}
💡 Used in:
- Dynamic table storage
- Graph adjacency matrices
- Image data buffers
🔹 Common Use Case 3: Linked Lists #
Each node is created dynamically to store data. Data can be stored in different memory locations without needing to be contigous.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
int main() {
// Linkedlist in Stack
// Node a, b, c;
// a.data = 1;
// a.next = &b;
// b.data = 2;
// b.next = &c;
// c.data = 3;
// c.next = NULL;
// LinkedList in Heap
Node* a = malloc(sizeof(Node));
Node* b = malloc(sizeof(Node));
Node* c = malloc(sizeof(Node));
a->data = 1;
a->next = b;
b->data = 2;
b->next = c;
c->data = 3;
c->next = NULL;
return 0;
}
⚠️ Memory Management Best Practices #
✅ Always check if malloc()
returned NULL
.
✅ Always free()
memory after use.
✅ Avoid memory leaks and dangling pointers.
✅ Use tools like Valgrind to detect leaks.
🚫 Common Errors #
Error | Cause |
---|---|
Segmentation fault | Using uninitialized or freed pointer |
Memory leak | Forgetting to call free() |
Double free | Freeing the same pointer twice |
Buffer overflow | Writing beyond allocated memory |
🧠 Summary #
Function | Use | Key Point |
---|---|---|
malloc() | Allocate memory | Uninitialized memory |
calloc() | Allocate + zero initialize | Slower, safer |
realloc() | Resize memory | Retains old data |
free() | Release memory | Must call manually |
💡 Exercises #
- Implement a dynamically growing array that doubles in size.
- Create a linked list of student records using dynamic memory.
- Write a function to dynamically allocate a 2D array.
- Simulate dynamic allocation of memory for a tree.
🏁 Takeaway #
Dynamic memory makes C powerful — but with great power comes great responsibility.
Always pair every malloc()
with a matching free()
!