Day 10: Dynamic Memory Allocation#
Overview#
So far, we’ve allocated memory at compile time (fixed-size arrays). Today we’ll learn how to allocate memory at runtime using malloc, calloc, realloc, and free. This is crucial for writing flexible programs.
What We’ll Learn Today#
- Why dynamic memory allocation
- malloc function
- calloc function
- realloc function
- free function
- Memory management best practices
- Avoiding memory leaks
- Common memory errors
Why Dynamic Memory Allocation?#
Problem with Static Arrays#
int arr[100]; // Always allocates 100 integers
// What if we only need 10? Wasteful!
// What if we need 1000? Too small!
Solution: Dynamic Allocation#
int n = 10; // Determined at runtime
int* arr = malloc(n * sizeof(int)); // Allocate exactly what we need
The malloc() Function#
Allocates a block of memory and returns a pointer:
void* malloc(size_t size);size: Number of bytes to allocate- Returns: Pointer to allocated memory (or NULL if failed)
Basic Usage#
#include <stdio.h>
#include <stdlib.h>
int main() {
// Allocate memory for 5 integers
int* ptr = malloc(5 * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Use the memory
ptr[0] = 10;
ptr[1] = 20;
ptr[2] = 30;
ptr[3] = 40;
ptr[4] = 50;
printf("Values: %d %d %d\n", ptr[0], ptr[1], ptr[2]);
// Free the memory when done
free(ptr);
return 0;
}Why Check for NULL?#
int* ptr = malloc(size);
// ✅ Always check
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}Allocation can fail if system runs out of memory!
Why sizeof()?#
Always use sizeof() to make code portable:
// ❌ Not portable - assumes int is 4 bytes
int* ptr = malloc(5 * 4);
// ✅ Portable - works on any system
int* ptr = malloc(5 * sizeof(int));
// For floats
float* fptr = malloc(10 * sizeof(float));
// For arrays of structures
typedef struct {
int id;
char name[50];
} Student;
Student* students = malloc(20 * sizeof(Student));The calloc() Function#
Allocates memory AND initializes all bytes to zero:
void* calloc(size_t num, size_t size);num: Number of elementssize: Size of each element- Returns: Pointer (or NULL if failed)
calloc vs malloc#
// malloc - uninitialized (garbage values)
int* arr1 = malloc(5 * sizeof(int));
printf("%d\n", arr1[0]); // Garbage value
// calloc - initialized to zero
int* arr2 = calloc(5, sizeof(int));
printf("%d\n", arr2[0]); // 0
free(arr1);
free(arr2);Example#
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
// Allocate array of integers, all initialized to 0
int* arr = calloc(n, sizeof(int));
if (arr == NULL) {
printf("Allocation failed!\n");
return 1;
}
// Print values (all are 0)
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n"); // Output: 0 0 0 0 0
free(arr);
return 0;
}The realloc() Function#
Resizes previously allocated memory:
void* realloc(void* ptr, size_t new_size);ptr: Previous pointer from malloc/callocnew_size: New size in bytes- Returns: Pointer to resized memory (may be new address)
Example: Growing an Array#
#include <stdio.h>
#include <stdlib.h>
int main() {
// Start with 3 integers
int* arr = malloc(3 * sizeof(int));
if (arr == NULL) {
printf("First allocation failed!\n");
return 1;
}
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
printf("First allocation: ");
for (int i = 0; i < 3; i++) printf("%d ", arr[i]);
printf("\n");
// Resize to 5 integers
arr = realloc(arr, 5 * sizeof(int));
if (arr == NULL) {
printf("Reallocation failed!\n");
return 1;
}
// Initialize new elements
arr[3] = 40;
arr[4] = 50;
printf("After realloc: ");
for (int i = 0; i < 5; i++) printf("%d ", arr[i]);
printf("\n");
free(arr);
return 0;
}Output:
First allocation: 10 20 30
After realloc: 10 20 30 40 50The free() Function#
Deallocate memory to prevent memory leaks:
free(ptr);
ptr = NULL; // Good practice
Important: Set to NULL After Freeing#
#include <stdio.h>
#include <stdlib.h>
int main() {
int* ptr = malloc(10 * sizeof(int));
free(ptr);
ptr = NULL; // Prevent use-after-free
// ❌ Don't do this after freeing
// printf("%d\n", *ptr); // Undefined behavior!
return 0;
}Memory Leaks#
A memory leak occurs when allocated memory is never freed:
Example of Memory Leak#
void leaky_function() {
int* ptr = malloc(100 * sizeof(int));
// ... use ptr ...
return; // ❌ Memory not freed! Leak!
}
int main() {
leaky_function();
leaky_function();
// Multiple leaks accumulate
return 0;
}Fixed Version#
void correct_function() {
int* ptr = malloc(100 * sizeof(int));
// ... use ptr ...
free(ptr); // ✅ Memory freed
return;
}Common Leak Pattern#
int main() {
int* ptr = malloc(100 * sizeof(int));
if (error_condition) {
printf("Error!\n");
return 1; // ❌ Memory not freed!
}
free(ptr);
return 0;
}Fixed:
int main() {
int* ptr = malloc(100 * sizeof(int));
int status = 0;
if (error_condition) {
printf("Error!\n");
status = 1;
}
free(ptr);
return status;
}Practical Example: Dynamic Array#
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("How many numbers? ");
scanf("%d", &n);
// Allocate array
int* arr = malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input
printf("Enter %d numbers: ", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// Calculate sum
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
// Calculate average
float average = sum / (float)n;
printf("Sum: %d\n", sum);
printf("Average: %.2f\n", average);
// Free memory
free(arr);
return 0;
}Dynamic Array of Structures#
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int id;
char name[50];
float salary;
} Employee;
int main() {
int count;
printf("How many employees? ");
scanf("%d", &count);
// Allocate array of Employee structures
Employee* emp = malloc(count * sizeof(Employee));
if (emp == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input
for (int i = 0; i < count; i++) {
printf("\nEmployee %d:\n", i + 1);
printf("ID: ");
scanf("%d", &emp[i].id);
printf("Name: ");
scanf("%s", emp[i].name);
printf("Salary: ");
scanf("%f", &emp[i].salary);
}
// Display
printf("\n=== Employee Records ===\n");
float total_salary = 0;
for (int i = 0; i < count; i++) {
printf("ID: %d, Name: %s, Salary: $%.2f\n",
emp[i].id, emp[i].name, emp[i].salary);
total_salary += emp[i].salary;
}
printf("Total Salary: $%.2f\n", total_salary);
// Free memory
free(emp);
return 0;
}Memory Management Best Practices#
1. Always Check for NULL#
ptr = malloc(size);
if (ptr == NULL) {
printf("Allocation failed!\n");
return -1;
}2. Free All Allocated Memory#
int* arr = malloc(n * sizeof(int));
// ... use arr ...
free(arr);3. Set Pointer to NULL After Freeing#
free(ptr);
ptr = NULL;4. Avoid Double Free#
free(ptr);
free(ptr); // ❌ Double free error!
5. Don’t Free Stack Variables#
int x = 10;
free(&x); // ❌ Wrong! x is on stack
6. Use sizeof() Consistently#
int* arr = malloc(n * sizeof(int)); // ✅ Correct
// NOT: malloc(n * 4); // ❌ Not portable
Common Errors#
| Error | Example | Fix |
|---|---|---|
| Memory leak | malloc() without free() | Always free() |
| Null pointer dereference | *NULL | Check for NULL |
| Double free | free() twice | Don’t free twice |
| Buffer overflow | Writing beyond allocated | Check bounds |
| Use-after-free | Use pointer after free() | Set to NULL |
Practice Exercises#
Exercise 1: Resizable Array#
Write a program that:
- Takes numbers until user enters -1
- Dynamically grows array as needed (use realloc)
- Displays all numbers
- Frees memory
Exercise 2: Matrix Operations#
Write a program that:
- Dynamically allocates 2D array (array of pointers)
- Inputs matrix values
- Calculates sum and average
- Frees all memory
Exercise 3: String Duplication#
Write a function that:
- Takes a string
- Allocates memory
- Copies string to new location
- Returns pointer
- Main function frees memory
Summary#
✅ Understood why dynamic allocation is needed
✅ Used malloc() for allocation
✅ Used calloc() for zero-initialized memory
✅ Used realloc() to resize memory
✅ Used free() to deallocate memory
✅ Avoided memory leaks
✅ Handled common memory errors
Key Points to Remember#
malloc()- allocate uninitialized memorycalloc()- allocate zero-initialized memoryrealloc()- resize existing allocationfree()- deallocate memory- Always check for NULL after allocation
- Use
sizeof()for portability - Set pointer to NULL after freeing
- Every
malloc()/calloc()/realloc()needs afree()
Next Steps#
Tomorrow we’ll learn about recursion - a powerful programming technique!