Dynamic Memory (malloc/free)
Managing memory allocation and deallocation at runtime
๐ Dynamic Memory Management
Dynamic memory allows runtime allocation and deallocation using malloc() and free(). This provides flexible memory usage for varying data sizes.
// Allocate memory for 10 integers
int *ptr = malloc(10 * sizeof(int));
// Use the memory...
free(ptr); // Release memory when done
Memory Management Functions
malloc()
Allocate memory block
ptr = malloc(size);
free()
Deallocate memory block
free(ptr);
realloc()
Resize memory block
ptr = realloc(ptr, new_size);
calloc()
Allocate and initialize to zero
ptr = calloc(count, size);
๐น Basic malloc() and free() Usage
malloc() dynamically allocates memory at runtime, and free() releases it. Dynamic memory allocation allows programs to request memory based on runtime needs rather than fixed amounts. The malloc() function returns a pointer to allocated memory, which you must later release with free() to prevent memory leaks. Proper memory management is crucial for efficient programs that handle varying data sizes and prevent resource depletion.
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i;
int *numbers;
printf("Enter number of elements: ");
scanf("%d", &n);
// Allocate memory for n integers
numbers = malloc(n * sizeof(int));
// Check if allocation was successful
if (numbers == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input values
printf("Enter %d numbers:\n", n);
for (i = 0; i < n; i++) {
scanf("%d", &numbers[i]);
}
// Display values
printf("You entered: ");
for (i = 0; i < n; i++) {
printf("%d ", numbers[i]);
}
// Free allocated memory
free(numbers);
numbers = NULL; // Good practice
return 0;
}
Output:
Enter number of elements: 5 Enter 5 numbers: 10 20 30 40 50 You entered: 10 20 30 40 50
๐น calloc() vs malloc()
calloc() allocates memory and initializes it to zero, while malloc() allocates uninitialized memory. The calloc() function takes two arguments: number of elements and size per element, calculating total bytes automatically. malloc() takes only the total bytes needed. Choose calloc() when you need zeroed memory; use malloc() for faster allocation when initialization isn't necessary. Understanding these differences optimizes memory management in your programs.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *malloc_array, *calloc_array;
int size = 5;
// malloc - uninitialized memory
malloc_array = malloc(size * sizeof(int));
// calloc - initialized to zero
calloc_array = calloc(size, sizeof(int));
printf("malloc array (uninitialized): ");
for (int i = 0; i < size; i++) {
printf("%d ", malloc_array[i]);
}
printf("\ncalloc array (zero-initialized): ");
for (int i = 0; i < size; i++) {
printf("%d ", calloc_array[i]);
}
// Free memory
free(malloc_array);
free(calloc_array);
return 0;
}
Output:
malloc array (uninitialized): 32767 0 4196352 0 4196432 calloc array (zero-initialized): 0 0 0 0 0
๐น realloc() for Resizing Memory
The realloc() function dynamically resizes previously allocated memory blocks. When your data grows beyond allocated space, realloc() adjusts memory size without losing existing data. This function copies data to new memory if resizing in place isn't possible. Using realloc() enables flexible data structures that grow or shrink during program execution, providing dynamic solutions for variable-sized collections.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *numbers;
int initial_size = 3;
int new_size = 6;
// Initial allocation
numbers = malloc(initial_size * sizeof(int));
// Initialize first 3 elements
for (int i = 0; i < initial_size; i++) {
numbers[i] = (i + 1) * 10;
}
printf("Initial array: ");
for (int i = 0; i < initial_size; i++) {
printf("%d ", numbers[i]);
}
// Resize the array
numbers = realloc(numbers, new_size * sizeof(int));
// Initialize new elements
for (int i = initial_size; i < new_size; i++) {
numbers[i] = (i + 1) * 10;
}
printf("\nResized array: ");
for (int i = 0; i < new_size; i++) {
printf("%d ", numbers[i]);
}
free(numbers);
return 0;
}
Output:
Initial array: 10 20 30 Resized array: 10 20 30 40 50 60
๐น Safe Memory Management
Implement best practices to prevent memory leaks and segmentation faults. Always check if malloc() returns NULL before using the pointer, indicating allocation failure. Match every malloc() with a corresponding free(), and avoid using pointers after freeing memory. Initialize pointers to NULL and set them to NULL after freeing. These practices protect your programs from crashes and ensure stable, reliable execution with proper resource handling.
โ Safe Memory Practices
// Always check malloc return value
int *ptr = malloc(size * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
exit(1);
}
// Use the memory...
// Free and nullify pointer
free(ptr);
ptr = NULL; // Prevents accidental reuse
โ Dangerous Practices
// Don't do this!
int *ptr = malloc(100 * sizeof(int));
// ... use ptr ...
free(ptr);
*ptr = 10; // Use after free - undefined behavior!
// Or this!
free(ptr);
free(ptr); // Double free - program crash!