C Programming Language: Functions — malloc(), calloc(), realloc(), and free()
Before diving in the subject, first we must know why it’s helpful to use dynamic memory allocation, for example, when:
- It’s not known how many objects of some kind is needed.
- It’s needed to have data structures of size known, only at runtime.
- An object with a lifetime is different than what automatic variables (int a; char c;…) are, because they’re scope-bound, meaning that when the execution ends, they die… And so the data stored. ;)
Since we now know this shit already (see? It was easy), let’s move on to these four (4) functions. These functions can be found in the <stdlib.h>
header file.
malloc()
This function allocates a size byte of memory. It returns a pointer (*) to the first byte, or if there is an error, it returns NULL (to ensure that the situation is out of memory). The format follows as:
(type *) malloc(sizeof(type));
type can be any variable type, such as int, char, float, etc…
Now, try compiling the following code ;)
#include <stdio.h>
#include <stdlib.h>int main(void)
{
int *p;
p = 0x42;
printf("Pointer before malloc(): %p\n", p);
p = (int *)malloc(sizeof(int));
printf("Pointer after malloc(): %p\n", p); return (0);
}
The output should be something like this:
Pointer before malloc(): 0x42
Pointer after malloc(): 0x53fe040
So… What’s going on in here?
int *p;
Declare a pointer pointing to nowhere.
p = ( int * ) malloc ( sizeof ( int ) );
The pointer itself (not the content) is assigned to a pointer type int that contains the memory address space for an int. With
sizeof()
, it gets the space of the type. In this case we get 2 bytes as size, because it's the real size of an int.
If we want to assign a value to the content, we can do something like this:
*p = 5;
The content of the pointer is assigned to an int value five (5).
Ok, how about this next snippet, what do you think it does?
int *ptr = malloc(sizeof(int) * 420);
It allocates 420 ints to the pointer.
Now that we know the basics of malloc(), let’s try making a program and see how it works.
#include <stdio.h>
#include <stdlib.h>#define NUM_ELEM (42)int main(void)
{
int *ptr;
ptr = (int *)malloc(sizeof(int) * NUM_ELEM); if (ptr == NULL)
{
printf("Memory allocation falied!\n");
}
else
{
printf("Everything is cool...\n");
} return (0);
}
The program is self explanatory. If it’s unable to find the requested amount of memory, malloc() will return NULL.
Wait, wait, wait…
If it fails to allocate memory, and we still try to do stuff with our program, bad things will happen… How do we fix this? By protecting it of course! This is a tiny hack, see if you can make sense of it:
if (!(int *ptr = malloc(sizeof(int) * NUM_ELEM)))
{
// ERROR CONDITION HERE
}
This way, if the malloc function returns NULL, the if statement will be TRUE (see the ‘!’ before ptr, it inverts the logic). Now you can handle the error if wanted… so if you want to exit the program or do other things, now you can! Neat, uh?
free()
In layman’s terms, free() is the opposite of malloc(). If malloc() allocates memory, what does free() do? It de-allocates the memory. ;)
Imagine you have a clusterfuck of a program, and while it runs, it keeps malloc()-ing forever… it’s said to leak memory, and this is bad. Avoid these leaks with free()ing the memory when you’re done with it! It’s really simple, just look at this…
int *ptr = malloc(sizeof(int) * NUM_ELEM);
free(ptr);
That’s it, really.
calloc()
Same principle as malloc(), but it’s used to allocate storage. The real difference between these two, is that calloc() initializes all bytes in the allocation block to zero, because it’s used to reserve space for dynamic arrays. It’s written like this.
(type *) calloc(num, size);
num specifies the size in bytes of one element to the second argument (size). If the partitioning is successful, the address is returned. If not, NULL is returned.
Look at the following snippet
int *array;array = (int *)calloc(5, sizeof(int));
What do you think it does? That’s right! It allocates an array of 5 elements, with the size of an int. Now let’s try something different, let’s use malloc().
int *array;array = (int *)malloc(sizeof (int) * 5);
However, with malloc(), the reserved areas are undefined. Try compiling the following program and see for yourself!
#include <stdio.h>
#include <stdlib.h>#define NUM_ELEM (5)void print_array(int *array, int size)
{
for (int i = 0; i < size; i += 1)
{
if (i > size)
{
printf("[%d]", array[i]);
}
else
{
printf("[%d], ", array[i]);
}
}
putchar('\n');
}int main(void)
{
int *ptr_calloc;
int *ptr_malloc; ptr_calloc = (int*) calloc(NUM_ELEM, sizeof(int));
ptr_malloc = (int*) malloc(sizeof(int) * NUM_ELEM); print_array(ptr_calloc, NUM_ELEM);
print_array(ptr_malloc, NUM_ELEM); return (0);
}
WHAT?! It crashed?
Ohhh… That’s ok, and you should know why (hint: do you really wanna print garbage?).
Also, take note that calloc() itself is slower than malloc, because of the time spent clearing up the content allocated in memory (initializing everything to NULL). It’s better to use malloc() if you want to allocate some memory and copy some stuff there.
Realloc is used to change the size of memory block on the heap.
Look at the following snippet int *ptr = malloc(10 * sizeof(int));
Now, if you want to increase the size of memory pointed to by ptr from 10 to 20, without losing the contents of already allocated memory, use the mighty realloc().
ptr = (int *)realloc(ptr, 20 * sizeof(int));
In this case realloc will allocate memory for 20 integers somewhere else and then copy the contents of the first 10 locations from here to the new place. It will also de-allocate the existing memory and return a pointer to the new memory. Keep in mind the following:
- 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 realloc() fails the original block is left untouched; it is not freed or moved.
Trivia
- calloc() is basically
malloc() + memset()
.
int *ptr = malloc(sizeof(int));
memset(ptr, 0, sizeof (int));
This behavior is pretty much the same as calloc().
- In C++, the operators used are new and delete, instead of malloc() and free().