Dynamic Memory Allocation in C
Dynamic memory allocation allows programs to allocate memory at runtime from the heap, providing flexibility in memory management.
"Dynamic memory allocation in C is the process of allocating memory at runtime using heap memory with functions like malloc, calloc, realloc, and free."
What is Dynamic Memory Allocation?
Dynamic memory allocation is the process of allocating memory at runtime. Unlike static memory allocation where memory is allocated at compile time, dynamic allocation allows programs to request memory from the heap during execution.
Memory Segments in C
- Stack: Automatic memory allocation for local variables
- Heap: Manual memory allocation using dynamic allocation functions
- Global/Static: Memory for global and static variables
- Code/Text: Memory for program instructions
malloc() Function
The malloc() function allocates a block of memory of specified size and returns a void pointer to the beginning of the block.
malloc() Syntax
void *malloc(size_t size);
Basic malloc() Usage
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
// Allocate memory for one integer
ptr = (int *)malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Use the allocated memory
*ptr = 42;
printf("Value: %d\n", *ptr);
// Free the allocated memory
free(ptr);
return 0;
}
Dynamic Array with malloc()
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i;
int *arr;
printf("Enter the number of elements: ");
scanf("%d", &n);
// Allocate memory for n integers
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input elements
printf("Enter %d elements:\n", n);
for (i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// Display elements
printf("Elements are:\n");
for (i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Free the allocated memory
free(arr);
return 0;
}
calloc() Function
The calloc() function allocates memory for an array of elements, initializes all bytes to zero, and returns a void pointer.
calloc() Syntax
void *calloc(size_t num_elements, size_t element_size);
Basic calloc() Usage
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5, i;
// Allocate memory for 5 integers and initialize to zero
ptr = (int *)calloc(n, sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Display initialized values (should be 0)
printf("Initial values:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// Assign values
for (i = 0; i < n; i++) {
ptr[i] = (i + 1) * 10;
}
// Display assigned values
printf("After assignment:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
free(ptr);
return 0;
}
malloc() vs calloc()
| Feature | malloc() | calloc() |
|---|---|---|
| Initialization | Garbage values | Zero initialized |
| Parameters | One (total size) | Two (count, size) |
| Speed | Faster | Slower (due to initialization) |
| Use case | Single large block | Arrays needing initialization |
realloc() Function
The realloc() function changes the size of previously allocated memory block and returns a pointer to the new block.
realloc() Syntax
void *realloc(void *ptr, size_t new_size);
Basic realloc() Usage
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr, *new_ptr;
int n = 3, i;
// Initial allocation
ptr = (int *)malloc(n * sizeof(int));
if (ptr == NULL) {
printf("Initial allocation failed!\n");
return 1;
}
// Assign initial values
for (i = 0; i < n; i++) {
ptr[i] = i + 1;
}
printf("Initial array:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// Reallocate to larger size
n = 6;
new_ptr = (int *)realloc(ptr, n * sizeof(int));
if (new_ptr == NULL) {
printf("Reallocation failed!\n");
free(ptr);
return 1;
}
ptr = new_ptr;
// Assign values to new elements
for (i = 3; i < n; i++) {
ptr[i] = i + 1;
}
printf("After reallocation:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
free(ptr);
return 0;
}
Reallocating to Smaller Size
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 10, i;
// Allocate memory for 10 integers
ptr = (int *)malloc(n * sizeof(int));
// Initialize
for (i = 0; i < n; i++) {
ptr[i] = i + 1;
}
printf("Original array:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
// Reallocate to smaller size
n = 5;
ptr = (int *)realloc(ptr, n * sizeof(int));
printf("After shrinking:\n");
for (i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
free(ptr);
return 0;
}
free() Function
The free() function deallocates the memory previously allocated by malloc(), calloc(), or realloc().
free() Syntax
void free(void *ptr);
Memory Leak Prevention
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr1, *ptr2, *ptr3;
// Allocate memory
ptr1 = (int *)malloc(sizeof(int));
ptr2 = (int *)calloc(5, sizeof(int));
ptr3 = (int *)malloc(10 * sizeof(int));
if (ptr1 == NULL || ptr2 == NULL || ptr3 == NULL) {
printf("Memory allocation failed!\n");
// Free any successfully allocated memory
free(ptr1);
free(ptr2);
free(ptr3);
return 1;
}
// Use the memory
*ptr1 = 100;
ptr2[0] = 200;
ptr3[0] = 300;
printf("Values: %d, %d, %d\n", *ptr1, ptr2[0], ptr3[0]);
// Free all allocated memory
free(ptr1);
free(ptr2);
free(ptr3);
printf("Memory freed successfully!\n");
return 0;
}
Dynamic Memory for Structures
Single Structure
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
int roll_number;
char name[50];
float marks;
};
int main() {
struct Student *student_ptr;
// Allocate memory for one student structure
student_ptr = (struct Student *)malloc(sizeof(struct Student));
if (student_ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input student details
printf("Enter roll number: ");
scanf("%d", &student_ptr->roll_number);
printf("Enter name: ");
scanf("%s", student_ptr->name);
printf("Enter marks: ");
scanf("%f", &student_ptr->marks);
// Display student details
printf("\nStudent Details:\n");
printf("Roll Number: %d\n", student_ptr->roll_number);
printf("Name: %s\n", student_ptr->name);
printf("Marks: %.2f\n", student_ptr->marks);
// Free allocated memory
free(student_ptr);
return 0;
}
Array of Structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Employee {
int id;
char name[50];
float salary;
};
int main() {
struct Employee *employees;
int n, i;
printf("Enter number of employees: ");
scanf("%d", &n);
// Allocate memory for array of structures
employees = (struct Employee *)malloc(n * sizeof(struct Employee));
if (employees == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Input employee details
for (i = 0; i < n; i++) {
printf("\nEmployee %d:\n", i + 1);
printf("Enter ID: ");
scanf("%d", &employees[i].id);
printf("Enter name: ");
scanf("%s", employees[i].name);
printf("Enter salary: ");
scanf("%f", &employees[i].salary);
}
// Display employee details
printf("\nEmployee Details:\n");
printf("ID\tName\t\tSalary\n");
printf("---\t----\t\t------\n");
for (i = 0; i < n; i++) {
printf("%d\t%s\t\t%.2f\n",
employees[i].id,
employees[i].name,
employees[i].salary);
}
// Free allocated memory
free(employees);
return 0;
}
Dynamic 2D Arrays
Array of Pointers Method
#include <stdio.h>
#include <stdlib.h>
int main() {
int **matrix;
int rows = 3, cols = 4, i, j;
// Allocate memory for array of pointers (rows)
matrix = (int **)malloc(rows * sizeof(int *));
if (matrix == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Allocate memory for each row
for (i = 0; i < rows; i++) {
matrix[i] = (int *)malloc(cols * sizeof(int));
if (matrix[i] == NULL) {
printf("Memory allocation failed for row %d!\n", i);
// Free previously allocated memory
for (j = 0; j < i; j++) {
free(matrix[j]);
}
free(matrix);
return 1;
}
}
// Initialize and display matrix
printf("Matrix elements:\n");
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
matrix[i][j] = (i + 1) * (j + 1);
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Free allocated memory
for (i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Single Block Allocation Method
#include <stdio.h>
#include <stdlib.h>
int main() {
int *matrix;
int rows = 3, cols = 4, i, j;
// Allocate single block of memory
matrix = (int *)malloc(rows * cols * sizeof(int));
if (matrix == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Initialize matrix using pointer arithmetic
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
*(matrix + i * cols + j) = (i + 1) * (j + 1);
}
}
// Display matrix
printf("Matrix elements:\n");
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
printf("%d ", *(matrix + i * cols + j));
}
printf("\n");
}
// Free allocated memory
free(matrix);
return 0;
}
Memory Management Best Practices
1. Always Check Allocation Success
// Good practice
int *ptr = (int *)malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
exit(1);
}
2. Always Free Allocated Memory
// Good practice
int *ptr = (int *)malloc(sizeof(int));
// Use ptr
free(ptr);
ptr = NULL; // Optional: prevent dangling pointer
3. Avoid Memory Leaks
// Bad practice - memory leak
void bad_function() {
int *ptr = (int *)malloc(sizeof(int));
// Function ends without freeing ptr
}
// Good practice
void good_function() {
int *ptr = (int *)malloc(sizeof(int));
if (ptr != NULL) {
// Use ptr
free(ptr);
}
}
4. Handle Reallocation Carefully
// Good practice
int *ptr = (int *)malloc(initial_size * sizeof(int));
if (ptr != NULL) {
// Use ptr
int *new_ptr = (int *)realloc(ptr, new_size * sizeof(int));
if (new_ptr != NULL) {
ptr = new_ptr;
// Continue using ptr
} else {
// Handle reallocation failure
free(ptr);
return ERROR;
}
free(ptr);
}
Common Memory Errors
1. Dangling Pointers
// Problematic code
int *ptr = (int *)malloc(sizeof(int));
free(ptr);
// ptr is now a dangling pointer
// *ptr = 10; // Undefined behavior
2. Double Free
// Problematic code
int *ptr = (int *)malloc(sizeof(int));
free(ptr);
free(ptr); // Double free - undefined behavior
3. Memory Corruption
// Problematic code
int *arr = (int *)malloc(5 * sizeof(int));
// Accessing beyond allocated memory
arr[10] = 100; // Memory corruption
Practical Examples
1. Dynamic Stack Implementation
#include <stdio.h>
#include <stdlib.h>
struct Stack {
int *arr;
int top;
int capacity;
};
struct Stack* createStack(int capacity) {
struct Stack *stack = (struct Stack*)malloc(sizeof(struct Stack));
if (stack == NULL) return NULL;
stack->capacity = capacity;
stack->top = -1;
stack->arr = (int*)malloc(stack->capacity * sizeof(int));
if (stack->arr == NULL) {
free(stack);
return NULL;
}
return stack;
}
int isFull(struct Stack* stack) {
return stack->top == stack->capacity - 1;
}
int isEmpty(struct Stack* stack) {
return stack->top == -1;
}
void push(struct Stack* stack, int item) {
if (isFull(stack)) {
// Double the capacity
stack->capacity *= 2;
stack->arr = (int*)realloc(stack->arr, stack->capacity * sizeof(int));
if (stack->arr == NULL) {
printf("Memory reallocation failed!\n");
return;
}
}
stack->arr[++stack->top] = item;
printf("%d pushed to stack\n", item);
}
int pop(struct Stack* stack) {
if (isEmpty(stack)) {
printf("Stack underflow!\n");
return -1;
}
return stack->arr[stack->top--];
}
void freeStack(struct Stack* stack) {
free(stack->arr);
free(stack);
}
int main() {
struct Stack* stack = createStack(2);
push(stack, 10);
push(stack, 20);
push(stack, 30); // This will trigger reallocation
printf("%d popped from stack\n", pop(stack));
printf("%d popped from stack\n", pop(stack));
freeStack(stack);
return 0;
}
2. Dynamic String Manipulation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* concatenateStrings(const char* str1, const char* str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
size_t total_len = len1 + len2 + 1; // +1 for null terminator
char* result = (char*)malloc(total_len * sizeof(char));
if (result == NULL) {
printf("Memory allocation failed!\n");
return NULL;
}
strcpy(result, str1);
strcat(result, str2);
return result;
}
char* reverseString(const char* str) {
size_t len = strlen(str);
char* reversed = (char*)malloc((len + 1) * sizeof(char));
if (reversed == NULL) {
printf("Memory allocation failed!\n");
return NULL;
}
for (size_t i = 0; i < len; i++) {
reversed[i] = str[len - 1 - i];
}
reversed[len] = '\0';
return reversed;
}
int main() {
char str1[] = "Hello";
char str2[] = " World";
// Concatenate strings
char* concatenated = concatenateStrings(str1, str2);
if (concatenated != NULL) {
printf("Concatenated: %s\n", concatenated);
free(concatenated);
}
// Reverse string
char* reversed = reverseString(str1);
if (reversed != NULL) {
printf("Reversed: %s\n", reversed);
free(reversed);
}
return 0;
}
Conclusion
Dynamic memory allocation is a powerful feature in C that allows flexible memory management. Understanding malloc, calloc, realloc, and free is essential for writing efficient C programs. Always remember to free allocated memory to prevent memory leaks and handle allocation failures gracefully! 🚀