Memory Management is one of the most critical aspects of programming in C/C++. In this lecture, we will discuss about two fundamental functions in C/C++ for the memory management i.e. malloc() and free().
Using malloc() function, we can dynamically allocate a large chunk of memry on heap by specifying its size in bytes.
To use the malloc() function, we need to include the stdlib.h
header file.
void* malloc(size_t size);
Here, size_t
is an unsigned integer type, and represents the size in bytes, that needs to be allocated from heap. The malloc()
function returns a void pointer, and we can cast this void pointer into a pointer of any type.
If successful in allocating the memory on heap, the malloc()
function returns a pointer pointing to the first byte of the allocated space. Otherwise, it returns a NULL pointer.
Let’s see an example,
int n = 5; int *ptr = (int*) malloc(n * sizeof(int));
If the size of an integer is 4 bytes, then the malloc()
function above allocated 5 * 4, which is 20 bytes. This amount of memory can hold the values of 5 integers. The malloc()
function returns a pointer of void type, pointing to the start of this memory location. We casted it into an integer pointer ptr
.
Now, using the pointer ptr
, we can access all the 5 inetegers allocated on the heap. For example, to assign a value to the first element, we can apply the dereference operator on the pointer ptr
like this:
*ptr = 55;
Or we can use the subscript operator i.e.
ptr[0] = 55;
It will assign the value 55 to first integer in the allocated memory.
As pointer ptr
is of integer type, so to access the second integer, we can increment the pointer ptr
by one and then dereference it like this:
*(ptr + 1) = 66;
Or we can use the subscript operator i.e.
ptr[1] = 66;
It will change the value of the second integer in the memory allocated on the heap.
Casting from a void pointer to a proper data type pointer is essential, so that we can access the individual elements in this allocated memory using pointer arithmetic.
If the malloc()
function fails to allocate memory on the heap, it will return NULL
. Therefore, we should always first check if the returned pointer is NULL
or not. Only then should we try to access the elements in the allocated memory. Like this,
int n = 5; int *ptr = (int*) malloc(n * sizeof(int)); if (ptr != NULL) { ptr[0] = 55; ptr[1] = 55; ptr[2] = 55; ptr[3] = 55; ptr[4] = 55; }
Once memory is allocated in the heap and we no longer need it after accessing it, we should deallocate that memory. For this, we can use the free()
function.
To use the free()
function, we need to include the header file stdlib.h
.
The syntax of the free()
function is as follows,
void free(void* ptr);
It takes a pointer as an argument and deallocates the memory to which this pointer points. It does not return any value. It will deallocate the memory that was allocated on the heap by using malloc()
. However, you need to ensure that the pointer you pass to the free()
function points to a location where memory was allocated on the heap using malloc()
or calloc()
. Like this,
int n = 5; int *ptr = (int*) malloc(n * sizeof(int)); free(ptr);
If you pass a null pointer to the free()
function, nothing will happen. On the other hand, if you pass a pointer pointing to a memory location that was already deallocated, or it was not allocated by malloc()
or calloc()
, it can result in undefined behavior. Therefore, it’s crucial to use the free()
function correctly to deallocate the memory that was allocated using malloc()
or calloc()
, and deallocation should only happen once. We should not attempt to free the same memory multiple times.
Here’s a full example where we will allocate 20 bytes on the heap, which can hold 5 integer values. Then, we can use the subscript operator to access each individual element held by this memory chunk. After that, we will deallocate this memory by passing the pointer to the free()
function.
#include <stdio.h> #include <stdlib.h> int main() { int n = 5; // Allocate 20 bytes on heap int *ptr = (int*) malloc(n * sizeof(int)); if (ptr != NULL) { // Memory successfully allocated using malloc // populate the array for (int i = 0; i < n; ++i) { ptr[i] = i + 10; } // print the array for (int i = 0; i < n; ++i) { printf("%d ", ptr[i]); } printf("\n"); } else { printf("Memory not allocated.\n"); exit(0); } return 0; }
Output:
10 11 12 13 14
There are certain things we need to keep in mind while using the malloc()
and free()
functions.
malloc()
can return null, so it’s always essential to check if the returned pointer is not null before trying to access the memory allocated by these functions.malloc()
and don’t deallocate it using the free()
function, this can cause memory leaks which, over the long run, can crash your application.free()
function, don’t call the free()
function on it again as it can lead to undefined behaviour.free()
function, assign the pointer to null. This prevents freeing the memory again and also helps avoid dangling pointers.The malloc() and free() functions provide a way to dynamically allocate and deallocate memory on the heap in C or C++, but it is essential to follow best practices to avoid memory leaks, undefined behavior, and other memory-related issues.