Functions malloc, calloc, realloc and free are used to allocate /deallocate memory on heap in C/C++ language. These functions should be used with great caution to avoid memory leaks and dangling pointers.
How are these functions different (or similar)?
Let us discuss the functions one by one.
malloc (Allocating uninitialized memory)
This functions allocates the memory and returns the pointer to the allocated memory. Signature of function is
void *malloc(size_t size);
size_t corresponds to the integral data type returned by the language operator sizeof and is used to represent the size (in bytes) of an object. It is defined (In string.h header in C language andheader in C++) as an unsigned integral type. It is just an indication that the type is used to hold number of memory bytes (and not usual unsigned int).
The below code allocates memory for 10 integers and assign the address of allocated memory (address of the first byte of memory) to int pointer ptr
int * ptr = (int*) malloc(10 * sizeof(int));
- If the system is not able to allocate the requested memory on heap then malloc returns NULL.
- If size is zero (malloc(0)), then malloc returns either a NULL pointer or a valid pointer which can be passed to free function for successful memory dealocation. The actual value depends on the implementation.
- malloc returns a void pointer which need to casted to appropriate type before dereferencing. (The way we typecasted it to int* above.
- Memory returned by malloc is not initialized an holds garbage value.
Because malloc can return a NULL pointer in case it is not able to allocate memory, The value returned is first checked against valid memory allocation before using the pointer.
int * ptr = (int*) malloc(10 * sizeof(int)); if(ptr == NULL) // Unable to allocate memory. Take Action. else // Memory allocation successful. can use ptr
While specifying the size absolute values should be avoided to make the code platform independent. For example: If you know that your compiler (plus machine) allocates 2 bytes to integers and you want to allocate memory for one integer, than also you should NOT write code like
int* ptr = (int*) malloc(2); // BAD CODE.. hardcode value 2
because this code will fail when will compile it on 4-byte machines (which allocates 4-bytes to integers). A good way is to always use sizeof operator
int* ptr = (int*) malloc(sizeof(int)); // BAD CODE.. hardcode value 2
It will get the actual size of int on machine and pass that value to malloc. Such mistakes are only committed by programmers new to C language.
calloc (Memory allocation + Initialization)
Calloc also allocates memory on heap like malloc does. The only difference is that calloc also initialize the memory with zero (malloc returns uninitialized memory).
Signature of calloc is:
void* calloc( size_t num, size_t size );
It will allocate memory to an array of num elements where each element is of size bytes. we need to pass two parameters to calloc because it need to assign zero to each elements (hence it need to know how many elements are there).
The below code will allocate memory of one integer on heap, initialize it with zero and store the address in pointer ptr.
int * ptr = (int*) calloc(1, sizeof(int));
Except for initialization part, everything we wrote about malloc is true about calloc also.
Due to the alignment requirements, the number of allocated bytes is not necessarily equal to (num*size). This is typical to the struct where individual fields are alligned to word boundaries.
Realloc (change the size of memory block on heap)
Suppose a pointer (say ptr) is pointing to a memory of 10 int on heap allocated using malloc as below.
int * ptr = (int*)malloc(10*sizeof(int));
You want to increase the size of memory pointed to by ptr from 10 to 20, without loosing the contents of already allocated memory. In this case you can call the realloc function. Signature of realloc is:
void *realloc(void *ptr, size_t size);
where ptr is the pointer to the previously (currently) allocated block of memory and size is the new size (in bytes) for the new memory block.
It is possible that the function will move the memory block to a new location because it is not able to allocate memory just after the existing memory block as shown in the picture below:
in this case realloc will allocate memory for 20 integers somewhere else and then copy the contents of first 10 locations from here to the new place. will deallocate the existing memory and return a pointer to the new memory. Hence ptr will change.
Programmer, should be aware with this fact because it may result in dangling pointers. consider the case below:
int * ptr1 = (int*) malloc(5 * sizeof(int)); int * ptr2 = ptr1; ptr2 = (int*) realloc(ptr2, 10 * sizeof(int)); // ptr1 may become a dangling pointer
Int this case both ptr1 and ptr2 are pointing to the same memory location.
When realloc is called, the memory location pointed to by both the pointers may get deallocated (in case the contiguous space is not available just after the memory block). ptr2 will now point to the newly shifted location on the heap (returned by realloc), but ptr1 is still pointing to the old location (which is now deallocated).
Hence, ptr1 is a dangling pointer.
- If pointer passed to realloc is null, then it will behave exactly like malloc.
- If the size passed is zero, and ptr is not NULL then the call is equivalent to free.
- If the area is moved to new location then a free on the previous location is called.
- If contents will not change in the existing region. The new memory (in case you are increasing memory in realloc) will not be initialized and will hold garbage value.
- If realloc() fails the original block is left untouched; it is not freed or moved.
Free (Free the memory allocated using malloc, calloc or realloc)
free functions frees the memory on the heap, pointed to by a pointer. Signature of free function is
void free(void* ptr);
- ptr must be pointing to a memory which is allocated using malloc, calloc or realloc.
- If ptr is called on a memory which is not on heap or on a dangling pointer, then the behavior is undefined.
- If ptr is NULL, then free does nothing and returns (So, its ok to call free on null pointers).
int x = 2; int* ptr = &x; free(ptr); //UNDEFINED. int *ptr2; // UN initialized, hence dangling pointer free(ptr2); //UNDEFINED int *ptr3 = NULL; free(ptr3); //OK.
Crashes in malloc(), calloc(), realloc(), or free() are almost always related to heap corruption, such as overflowing an allocated chunk or freeing the same pointer twice.