Let’s take a look at the four methods that allow us to utilize dynamic memory in C. Dynamic memory just means we are using memory on the heap, instead of on the stack.
Why would you want to use dynamic memory?
You might want to create a variable or object that persists beyond the scope of the function it is created in (i.e. to share it between functions). The only way to do this is to create it dynamically, and then remember to deallocate it at a later point. Alternatively, you might not know the size of something you are using until runtime (e.g. reading in a file), in which case the heap is a better place to store your data. Why? Because the size of the stack is limited and it may be too small for unknown data processed at runtime.
How do you dynamically allocate in C?
There are three functions for memory allocation (plus one for deallocation). Each allocator returns a void* to the memory that has been allocated, which can be cast to the appropriate type.
Let’s look at each in turn:
1. malloc
This is the most commonly used method. Simply pass in how big you want your memory to be (in bytes), and you get a pointer to that memory back. The memory is uninitialized. If it fails it returns NULL.
2. calloc
Instead of passing in a size, you tell calloc how many of a certain type of variable you are going to use. E.g. 10 ints, or 16 structs. The memory is initialized to zeros. If it fails it returns NULL.
3. realloc
This method resizes an existing block of memory and you can make your existing memory allocation bigger or smaller. It frees the existing block and returns a void* to the new block. If you pass in zero, it effectively frees the memory in question. If it fails it returns NULL (see the comments in the code below for why you should pay careful attention to how you use realloc).
And what do you do when you’re finished?
You call free, nice and easy, and your memory is released (although it is not “deleted” as such – the data may exist until something else overwrites it, or part of it, or until the program ends).
Note that calls to malloc, calloc and realloc set errno if they fail. You can find out how to use errno here!
Can I see an example?
Sure – here’s a little bit of code that uses all four methods so you can see them at work:
#include <stdlib.h> #define BIG_NUMBER 1024 #define SMALL_NUMBER 16 struct msg { int code; char message[BIG_NUMBER]; }; int main(void) { char* buffer; struct msg* messagelist; /* Allocate some memory from the heap */ buffer = (char*)malloc(BIG_NUMBER); if (buffer != NULL) { /* I can use the memory safely */ } /* Reduce the size of the memory */ char* smallbuffer = (char*)realloc(buffer, SMALL_NUMBER); if (smallbuffer != NULL) { /* I can use the memory safely */ } /******************************************* * NOTE: Look carefully at the realloc call above. * If the call to realloc had failed and I had assigned * it to the original buffer like so: * buffer = (char*)realloc(buffer, SMALL_NUMBER); * then my buffer would have been set to NULL and I would * not only lose access to the data that was stored * there, but I'd create a memory leak too! *******************************************/ /* Allocate some memory from the heap */ messagelist = (struct msg*)calloc(SMALL_NUMBER, sizeof(struct msg)); if (messagelist != NULL) { /* I can use the memory safely */ } /* Remember to clear up after myself */ free(smallbuffer); free(messagelist); /* NOTE: I DON'T need to free the 'buffer' variable */ /* because realloc already did it for me :-) */ return EXIT_SUCCESS; }
assuming a situation here,say malloc worked ,and the realloc failed in this code , then shouldnt we also have free(buffer) at the end of the code?
If realloc fails you are left with an intact buffer, which you can still access. It isn’t a leak as such, so you don’t need to explicitly free this buffer until you know you aren’t going to use it again. It is only if you try to realloc to the SAME buffer that you get a memory leak because on failure realloc marks the ‘new’ buffer as NULL. This is exactly the point of my post 🙂
You could add a free() call to the buffer in question on realloc failure – but not at the end of the program or you could potentially get a double free. The only trouble with this is that if you free your buffer on a realloc failure, you lose that buffer for the rest of the program. It’s one of the pitfalls of realloc.