In this tutorial, we are going to learn about the concepts of dynamic memory allocation also known as DMA.
In C, memory allocation can happen statically (during compile time), automatically, or dynamically (during program execution or run-time).
C Dynamic Memory Allocation:
The memory allocation for all global and static (variables declared using static keyword) is done statically. The memory for automatic variables is allocated on the stack and is done automatically during function calls.
C provides below 4 the library functions to manually manage dynamically allocated memory:
- malloc() – used to allocate a specified number of bytes as a block of memory from the free store (which is also referred to as a heap)
- calloc() – used to allocate a specified number of bytes from the free store and also initialize them to zero.
- realloc() – used to increases or decreases the size of the specified block of memory.
- free() – used to release the specified block of memory to the system.
For static-duration and automatic-duration variables, the size of the allocation must be available at compile-time. However, there may be scenarios where we may want to allocate memory depending on user input during run-time. In such cases, dynamic memory allocation (DMA) is required.
Note: Here are some of the key points that are to be remembered.
- Static variables are created at compile-time and the size of the variables can not be altered.
- Stack variables are created when a function is called and destroyed when the execution of that function is complete.
- Variables whose memory is allocated on the heap are created and destroyed by the user according to the need.
- The size of the heap variable is allocated at run-time.
Why DMA?
The problems we encounter in C are :
- A variable’s value can be accessed or changed by another pointer variable and the actual variable has no knowledge about the change.
- If an array is declared once with some size, it cannot be modified anywhere else in the program. The memory can be allocated only once at compile time or run time.
The above problems can be solved by DMA (Dynamic memory allocation) using the heap memory.
Understanding malloc() function:
The malloc() function is used for memory allocation during the time of execution. It takes only one integer argument which represents the number of bytes to be allocated. The malloc() function returns the base address to the block of memory allocated on the heap.
The return type of that address is void *, hence in the code, we need to typecast that void * type to the primitive data type of the pointer variable.
Below code shows the usage of malloc() syntax:
(void *) malloc(number of bytes);
Let us consider an example:
float *p; // Line 1
p = (float *) malloc (sizeof (float)); // Line 2
In Line 1, the pointer variable p of type float occupies 2 bytes in memory.
In Line 2, the call to malloc() function returns 4 bytes of heap memory.
Since the default return type of malloc() is void *, we are typecasting it to float.
If it is unable to find the requested amount of memory, the malloc() function returns NULL. The macro NULL means that the pointer does not refer to a valid object.
The memory which is allocated by malloc() is by default uninitialized, meaning can contain garbage value.
Let’s consider an example code:
Below given code helps us in calculating the sum of two numbers:
#include <stdio.h>
#include <stdlib.h>
void main() {
int *a, *b;
a = (int*)malloc(sizeof(int)); //allocating memory
b = (int*)malloc(sizeof(int)); //allocating memory
printf("Enter any two integer values : ");
scanf("%d %d", a, b);
printf("Sum of two integers %d and %d is %d\n", *a, *b, *a + *b); //prints sum of values stored in a and b
}
Output:
Enter any two integer values : 5
2
Sum of two integers 5 and 2 is 7
Understanding calloc() function:
The calloc() function is used for memory allocation during the time of execution. This function takes two arguments, the first argument represents the number of objects to be allocated and the second argument represents the number of bytes to be allocated for each object.
The calloc() function returns the base address of the contiguous memory locations to which it is allocated. The return type of address is of type void, that’s why programmers have to typecast that void type to any other primitive data type depending on the pointer variable.
The memory which is allocated by calloc() function by default assigns initial values as zeros.
The general format of calloc() is:
(void *) calloc(number of objects, bytes of each object);
Let us consider the below example:
int *p;
p = (int *) calloc(10, sizeof (int));
Here pointer variable p is allocated with a memory of 2 bytes to save the base address returned by calloc() function.
The calloc() creates the heap memory for 10 objects, each with 2 bytes of memory, and returns the base address of the entire memory.
Let’s consider an example code:
Below given code helps us in creating a one-dimensional array with size given at run-time by using calloc() and display it.
#include <stdio.h>
#include <stdlib.h>
void main() {
int *a, n, i;
printf("Enter size of the array : ");
scanf("%d", &n);
a = (int*)calloc(n, sizeof(int)); //allocation memory
printf("Enter %d elements : ", n);
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
printf("The given array is : ");
for (i = 0; i < n; i++) {
printf("%d", a[i]);
}
}
Output:
Enter size of the array : 4
Enter 4 elements : 11
12
13
14
The given array is : 11 12 13 14
Understanding realloc() function:
The realloc() function is capable of increasing or decreasing the memory that has been allocated previously at run time.
The general format of realloc() is:
(void *) realloc (pointer variable, number of bytes);
The first argument of realloc() is a pointer to a block of memory of which the size is to be altered. The second argument specifies the new size.
If the allocation is successful, the returned value is again the pointer variable to the first byte of the allocated memory retaining the old contents.
Let us consider the below example:
int *p;
p = (int *) calloc(3, sizeof (int));
......
p = (int *) realloc (p, 5);
Here pointer variable p is allocated with the memory of 2 bytes to save the base address returned by calloc() function.
The calloc() creates the heap memory for 3 objects, each with 2 bytes of memory, and returns the base address of the entire memory.
After execution of some code, the programmer wants to increase the size of the array to 5 regions then realloc() function is used to increase 3 regions into 5 regions.
Let’s consider an example code:
Below given code helps us in creating a one-dimensional array with size given at run-time by using calloc(), change the size by using realloc(), and display it.
#include <stdio.h>
#include <stdlib.h>
void main() {
int *p;
p = (int*) malloc(sizeof(int));
*p = 35;
printf("Given integer value : %d\n", *p);
p = (int*) realloc(p,3);
*(p + 0) = 17;
*(p + 1) = 34;
*(p + 2) = 51;
printf("After resizing the values are : %d %d %d\n", *(p + 0), *(p + 1), *(p + 2));
}
Output:
Given integer value : 35
After resizing the values are : 17 34 51
Understanding free() function:
The memory obtained by malloc(), calloc(), and realloc() functions can be free when that space is not required for any storage. The function free() does this job.
The general format of free() is:
free(pointer variable);
Let us consider an example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *P;
P = (int *) malloc (sizeof (int));
if (P == NULL) { /* failed to reserve memory */
printf ("Failed to allocate space for %d bytes", sizeof (int));
return 1;
}
*P = 15;
printf ("%d\n", *p);
free(P); /* memory is released */
return 0;
}
Output:
15
In the above example, the pointer variable p is pointed to the memory created at run time, access the value at that address, and finally, release the memory when it is not required by using the free() function.
References:
Happy Learning 🙂