While declaring arrays, we noticed that the array size must be specified at compile-time and cannot change during the program’s duration. The actual size needed by the array is often not known until runtime because the amount of space required depends upon input data.
This approach is although simple but has many disadvantages. With this approach, a lot of memory is wasted when the number of elements required is minimal. It is also incapable of handling problems large than the size specified. So the solution to this problem is to allocate memory to the array dynamically so its size can be changed during runtime. This process of memory allocation to variables at run time is called dynamic memory allocation in C.
C Dynamic Memory allocation is performing manual memory management by a group of functions in the standard C library, i.e. malloc, realloc, calloc and free. These functions are defined in stdlib.h header file.
We’ll be covering the following topics in this tutorial:
Types of Memory Allocation in C Language
The C programming language allocates memory three ways statically, automatically, or dynamically.
Static Memory Allocation in C
Static variables are assigned across the main memory, typically along with the program executable code, and remain during the program life. Array is an example of static memory assignment, while linked list, queue and stack are examples for the dynamic memory allocation.
Automatic Memory Allocation in C
Automatic variables on the stack are allocated, and change as functions are called. The memory allocation size must be compile-time constant for static and automatic variables. If the appropriate size is not defined before runtime, it will be inefficient to use fixed-size data objects.
Dynamic Memory Allocation in C
The length of the allocated memory can also be concerning. For all cases, no static or automatic memory is sufficient. Automatically assigned memory can not persist for several functions calls although static memory remains unchanged throughout the program life. In certain instances, the programmer needs more flexibility in the managing of the lifetime of the memory allocated.
These limitations are prevented by using dynamic memory allocation in C, in which memory is managed more explicitly, usually through the allocation from “heap,” a memory area organised for this purpose. In C, the Malloc library function assigns a memory block to the heap. The program accesses this particular memory block by using a pointer returned by malloc. The pointer has been moved to free after the memory is no longer needed so that the memory can be used for different functions.
Program execution with dynamic memory allocation is slower than with the use of static memory allocation. It’s because the memory needs to be allocated during runtime in dynamic memory allocation. It slows down the program execution.
The functions of dynamic memory in C are defined in the stdlib.h header.
malloc() Function
The malloc() is the simplest standard library function that allocates a contiguous block of memory of specified size at run time. It takes the following form,
void *malloc(size_t size);
Here size is the number of bytes of storage to be allocated. The size_t corresponds to the data type, which is equal to the unsigned int data type. If the desired amount of memory is available. The malloc() function returns a void pointer to the first byte of a newly allocated memory block. You can then use this pointer to access the data. Since it returns a void pointer, it is necessary to explicitly typecast to the appropriate data type before using them to access the allocated memory. So while writing programs, it is generally recommended to check any dynamic memory request immediately using an if statement to make sure the memory is there before you can try to use it. Moreover, the allocated block of memory is not initialized—for example, the following statements.
int *ptr; ptr = (int *) malloc(20);
Allocates 20 bytes of storage and stores the address of the first byte in ptr. This whole block can hold 10 int values as if each int type requires 2 bytes. The (int *) written before the malloc() function is used to convert the address returned by the function to the type ‘pointer to int’.
Generally, programmers work with data types and not bytes, so to allocate a block of memory for 10 items of type int, we write the following statement,
int *ptr = (int *)malloc(10*sizeof(int));
If the memory you requested is not available for any reason, malloc () returns a pointer with the NULL value. So to check whether the memory is available, we use the if statement as follows,
if(ptr == NULL) { /*code that handles if no memory is allocated*/ }
calloc() Function
The calloc() function is another alternative to the malloc() function. Like the malloc() function, it also allocates a block of memory at run time. Still, unlike the malloc() function, which takes one parameter, this function takes 2 parameters: the number of elements to allocate and each element’s size. From these values, it computes the total number of bytes needed. It takes the following form,
void *calloc(size_t nitem,size_t size);
Here, nitem is the number of allocated elements, each of which is size bytes long. Like malloc(), calloc() also returns a void pointer to the first byte of the newly allocated memory block if the desired amount of memory is available. However, the major difference between malloc() and calloc() is that calloc() initializes all bytes in the allocated memory block to 0 before returning a pointer to it. If enough memory is unavailable, it returns a NULL pointer. So the statement,
int *ptr = calloc(10,sizeof(int));
realloc() Function
realloc() function enables you to change the size of the previously allocated block of memory by malloc(), calloc() or by itself. It takes the following form,
void *realloc(void *ptr, size_t size);
Here ptr points to the memory block obtained by the previous call of malloc(), calloc() or realloc() and size represents the new size of the memory block (in bytes), which may be larger or smaller than the original size.
Suppose the new size is large and enough free memory is present immediately after the previously allocated block. In that case, its old contents remain unchanged and additional memory is added to the block’s end. However, this function does not initialize the bytes that are added to the block. If the new size is more extensive and sufficient space is not available after the block, realloc() allocates a new block of the right size. It copies the contents of the existing block to the newly allocated block. On the other hand, if the size is smaller, the memory block contents are preserved upto the lesser of the new and old sizes.
If the requested block of memory is unavailable, it returns a null pointer and data in the old memory block is unchanged.
In case the first argument of the realloc() is a null pointer, then it behaves exactly like malloc(). If the second argument (i.e. size) of realloc() is 0, it frees the memory block, and the null pointer is returned.
free() Function
Memory is a limited resource. One should allocate exactly the required piece of memory before you need it and release it as soon as you don’t need it to be reused.
The free() function is used to deallocate memory space allocated previously by a call to the malloc(), calloc() or realloc(), making it available again for further allocation. If the pointer is null, it does nothing. Its declaration is of the form,
void free(void *ptr);
ptr points to the block of memory allocated with the memory allocation functions discussed earlier. For example, the statement,
int *ptr z (int*)malloc(10*sizeof(int)); free(ptr);
releases the block of memory that ptr points to. This block is then returned to the free pool (heap), where it becomes available for reuse in subsequent calls to malloc(), calloc() and realloc().
Differences between malloc() and calloc()
malloc() | calloc() |
malloc() does not initialise the allocated memory. | calloc() ensures that all bytes of the allocated memory block is initialised to 0. |
malloc() Takes one argument. | calloc() requires two arguments. |
malloc() assigns the memory’s random data. | calloc() allocates the memory to zero. |
memset() and malloc() may be used to get the same effect as calloc(). |
The array size is, however, determined at compile time. If you want to assign a similar array dynamically, you can use the following code:
int *array = malloc(10 * sizeof(int));
This calculates the number of bytes in the memory of the ten integers and then requests for many bytes from malloc and sets the result to a named array pointer.
Because Malloc may not be able to return the request, a null pointer could be returned and it is good programming practise to check:
int *array = malloc(10 * sizeof(int)); if (array == NULL) { fprintf(stderr, "malloc failed\n"); return -1; }
If the program does not need the dynamic array anymore, it should eventually call free to return the memory it occupies:
free(array);
The memory allocated by malloc is not initialised and may contain cruft: the remaining data used and discarded previously. After malloc is assigned, uninitialized variables are the elements of the array. The calloc command returns an assignment that has already been cleared:
int *array = calloc(10, sizeof(int));
We can resize the memory size a pointer points to with realloc. For example, if we have a pointer acting as a size n array and want to change it to a size m array, we can use realloc.
int *arr = malloc(2 * sizeof(int)); arr[0] = 1; arr[1] = 2; arr = realloc(arr, 3 * sizeof(int)); arr[2] = 3;
Note that relocation should be assumed to have changed the block’s base address. Any pointers to addresses within the original block are therefore no longer valid.
Example of malloc() function in C
By defalut for malloc() function return type is void. The heap area is made up of hash codes. i.e., Address may or may not be stack. That is why malloc() assigns heap memory.
// Program to calculate the sum of n numbers entered by the user #include <stdio.h> #include <stdlib.h> int main() { int number, i, *arr, total = 0; printf("Enter number of elements: "); scanf("%d", &number); arr = (int*) malloc(number * sizeof(int)); // if memory cannot be allocated if(arr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Enter elements: "); for(i = 0; i < number; ++i) { scanf("%d", arr + i); total += *(arr + i); } printf("Sum = %d", total); // deallocating the memory free(arr); return 0; }
Example of calloc() function in C
// Program to calculate the total of n numbers entered by the user #include <stdio.h> #include <stdlib.h> int main() { int number, i, *arr, total = 0; printf("Enter number of elements: "); scanf("%d", &number); arr = (int*) calloc(number, sizeof(int)); if(arr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Enter elements: "); for(i = 0; i < number; ++i) { scanf("%d", arr + i); total += *(arr + i); } printf("Sum = %d", total); free(arr); return 0; }
Example of realloc() function in C
#include <stdio.h> #include <stdlib.h> int main() { int *arr, i , n1, n2; printf("Enter size: "); scanf("%d", &n1); arr = (int*) malloc(n1 * sizeof(int)); printf("Addresses of previously allocated memory: "); for(i = 0; i < n1; ++i) printf("%u\n",arr + i); printf("\nEnter the new size: "); scanf("%d", &n2); // rellocating the memory arr = realloc(arr, n2 * sizeof(int)); printf("Addresses of newly allocated memory: "); for(i = 0; i < n2; ++i) printf("%u\n", arr + i); free(arr); return 0; }
Summary
• We can manage the memory dynamically by creating memory blocks as necessary in a heap.
• Memory is allocated at runtime in dynamic memory allocation.
• Dynamic memory allocation enables the manipulation of strings and arrays whose size is flexible and can be modified in your program at any time.
• It is necessary when you have no idea how much memory a specific structure will occupy.
• Malloc is a dynamic memory allocation function, which means that memory blocks have been initialized into a garbage value, with a specific size.
• Calloc is a contiguous memory assignment function assigning multiple memory blocks to an initialized time of 0.
• Realloc is used to the reallocation of memory by specified size is used.
• The free function is used for free the memory spaces allocated by malloc() and calloc().