Stop Using malloc! What Every Good Developer Needs to Know About Memory Allocation

Marius Debussche
CodeX
Published in
5 min readAug 11, 2021

For the newer programmers among us, the malloc function can be a bit daunting. Let’s try to break it down. A running program needs space to save and read variables, so your computer provides two different types of location to put them: the Stack and the Heap. The Stack is used for general purposes and is handled automatically by the program, whereas the Heap is managed by the programmer. Let's have a look at a quick example:

In this basic function, the definition of the variable a is made on the Stack. The value 3 is just pushed on top of it and the C++ compiler remembers its address when it needs to be accessed. Once our print_example function returns, the variable runs out of scope and everything that was declared inside is discarded and cannot be accessed anymore. Now, the scope is a basic concept and everyone should already be familiar with that.

Let us try to challenge the compiler. Let’s say we need a function that initializes a variable and returns a reference to it, which is often the case in C where there is no such thing as classes and constructors.

Can you guess what will be printed on the console? The compiler doesn’t seem to like it:

The program compiles but throws a segmentation fault, meaning we tried to access a location we shouldn’t have. This doesn’t work because as we said, the variable a is discarded after the function returns, so the pointer to a we got in the main function is dangling. By the way, it’s interesting to notice that the compiler only throws a warning and lets us run the program. Yes, warnings are relevant and shouldn’t be ignored.

The Call Stack, before and after the function returns

So if our previous doesn’t work, how do we initiate a variable outside of our main scope? To do this properly, we actually need to allocate space for the variable on the Heap using malloc. Here is the corrected function:

If you’ve never seen a call to malloc, this line should look pretty scary, but it's really not. malloc returns the address of a free location on the Heap, of the requested size (in bytes). For our example, we need an int so we ask for memory the size of an int. malloc is a general-purpose memory allocation, (not just for integers) so it returns a generic pointer of type void*. We need to cast this pointer to what we need, here an int*.

Now, when the function returns, the variable doesn't get discarded because it is not part of the context of the function, but rather the program itself. Thus, the variable is accessible everywhere, even though it is not a global variable! We now get the wanted output: a=3 .

When a variable is allocated on the Heap, it is important to free that memory with free(pa)once we do not need it anymore, or we might end up with memory leaks. This very annoying problem is hard to suppress totally for large applications, as it requires meticulousness. If you forget about it and run your code, don’t worry, the memory won’t be gone forever as the memory allocated for the Heap of a program is discarded once it terminates. That is why memory leak is even more annoying when a program is running for extended periods of time, like video games for example.

For those who want to know more about the differences between the Stack and the Heap (and really, you should) I recommend taking a look at this article. You will also learn when to use the Heap instead of the Stack. You should remember two things if you’re wondering when to use either one:

  • avoid create huge objects on the Stack (because its size is limited and might lead to a stack overflow)
  • be careful to the scope of the variables and ask yourself if the pointer you’re returning is not dangling

So this is basically how to use heap memory allocation. It’s actually not that complicated to understand, we request memory of a given size, write to it, and free the space when we don’t need it anymore. The difficult part is to be watching for these memory leaks, which can be a real pain to deal with, because of unexpected paths the program can take. For example, if a function throws an exception and returns early, the free instruction might never be reached, and the memory it is associated with becomes useless. Because of the difficulty of handling raw malloc and free, it should be avoided, except if we really know what we’re doing or if our application is simple enough (and even then, we can always be surprised).

So what are the solutions to this problem? One possibility is to use the RAII programming technique, which stands for Ressource Acquisition Is Initialisation. It was first introduced by Bjarne Stroustrup, the creator of the C++ programming language himself! Basically, we initiate the memory at the same time we do the acquisition of the object. That way, the allocated memory is bound to the lifetime of the object, meaning that if it runs out of scope and is discarded, the destructor of the object will clean the heap-allocated memory for us. That might not be very clear right now so let's look at what a memory allocator class could look like.

When the allocator is constructed, it allocates memory and stores its address. When it is destructed, the memory is automatically freed. So when the MemoryAllocator instance runs out of scope and is discarded, its destructor is called and the allocated memory will be freed, without us worrying about freeing that memory ourselves. It is pretty straightforward to use: we instantiate the object and request the location it was given. Here is how an array could be initialized on the Heap:

Now, If every single heap allocation is made through RAII, we don’t have to worry about memory leaks anymore. Even when a function throws an exception, its local variables are discarded and so is the MemoryAllocator instance.

RAII has really become the norm now, and modern programming languages such as Rust forces the programmer to use it, by associating the scope of an object and its existence. This is called the lifetime of an object. If you want to learn more about safe programming techniques, I recommend trying yourself at Rust, you might discover programming from another point of view.

Thank you for reading my first article where I try to break down intermediate concepts in programming! If you liked it and want me to tackle other concepts, let me know and like this article, it means the world to a writer.

--

--

Marius Debussche
CodeX
Writer for

Programmer over all, I keep discovering new stuff and writing about it.