DYNAMIC MEMORY ALLOCATION
Dynamic Memory Allocation
Dynamic memory allocation is the process of assigning space for variables at run time. This is done automatically for function-scope variables which are put onto the stack when they are defined, but dynamic memory allocation usually refers to the process of using one of the malloc-family functions to manually allocate space of the heap.
The biggest risk with dynamic memory allocation is forgetting to free the memory after your finished with it. This can quickly lead to you running out of memory as more and more spaces in memory are assigned to the same variable (this is called a memory leak).
The malloc-family of functions include
free(). Most C-programming IDEs link to a standard library with these functions. They are used by including
stdlib.h into your project. These functions are also available in C++.
The thread-safety of the memory functions listed below is implementation dependant. This means that their safety when used in systems with threads (or tasks) is not guaranteed. This normally is the case when you are running an operating system. The danger is especially relevant for memory functions on embedded systems running operating systems, as while memory functions on computers would be written with threading in mind, this is not the case for embedded systems.
The best thing to do is to wrap the functions in thread safe procedures (such as a scheduler suspender call), or write your own thread safe versions from scratch.
malloc() is the most-known about memory allocation function. Given a size (in bytes), it allocates a block of memory and returns a pointer to this memory.
malloc() always returns a
void* pointer, so it has to be cast into the desired pointer type.
Memory allocated in this fashion persists until it is freed (it is placed on the heap). This means that unlike local variables, it will persist even after the function that called
malloc() has ended. This can lead to memory leaks if you are not careful and keep track of all of your allocated variables. C does not have any garbage collection.
malloc(), remember that it requires the number of bytes you wish to allocate in memory. This is NOT necessarily the same as the number of elements you want in your array. For example, if your array is of type
uint32_t, and you want the array to store 10
uint32_t numbers, you need to allocate 40 bytes, since each
uint32_t requires 4 bytes of memory. The safest way to make sure this does not trip you up when programming is to use the
sizeof() command as shown in the below code.
realloc() is used for reallocating memory to a different size. It takes a pointer to a memory address that has been previously allocated with malloc(), and the desired new size of the memory, and returns a pointer to the newly-sized memory block.
You must consider that if
realloc() cannot allocate enough memory at the end of the current block, it will re-allocate the entire block somewhere else, and will return a pointer to a different address in memory. Take care in making sure that any other pointers to the old memory allocation will need updating also. One way to stop this problem occurring is to take pointers to the original pointer instead of directly to the block in memory.
alloca() is used to allocate memory on the stack, not the heap. Unlike the
malloc()-family of functions, memory allocated by
alloca() is automatically de-allocated when the function returns to the caller. It also has the benefit of not causing memory fragmentation. However, it can cause stack overflows if you assign too much memory, it does not check to see whether the operation is safe before it goes ahead an assigns the memory. For this reason it is not recommended for variables larger than a couple of hundred bytes (this is platform dependant).
The function declaration:
size is the number of bytes you wish assigned (positive integer).
You can use
alloca() in your code after including
alloca.h in your source file (
Do not free a variable that has been allocated memory with
free()! This will most likely crash your program (behaviour is undefined).
If you are using GCC (and the glibc library), you should note that in most cases, the GCC compiler will inline the
alloca() call and remove the function. It is normally replaced with just a single line of code that adjusts the stack pointer.
On many systems, you cannot call
alloca() from within the list of arguments for a function call, as the memory allocated would be interspersed with the arguments when the function is entered.
alloca() is used in the GNU glibc function
getopt() (used for command-line option processing).
Appending An Element To An Array
When using dynamic memory allocation, you commonly want to add a new element to the end of an array that had been dynamically allocated yourself, and initialise it to 0. This can be done with clever use of the realloc() and memset() functions. The code below shows a function which can does this for you. This essentially increase the array size by 1.
Memory Allocation On Embedded Systems
And why it can be a bad idea.
Memory allocation is usually treated with great caution on embedded systems, with many embedded design standards or companies banning the use of it outright. Why is this?
The first reason is that memory allocation can fail. There is no guarantee that a call to
malloc()will be successful. The spec says that if
malloc()cannot find enough space to satisfy the memory request, it will return
NULL. What should you do when
NULL? For most applications running on desktops/servers, this will just mean the application will terminate, which could cause some annoyance for the users/customers of the application. However, if your embedded code is controlling the thrust of a rocket’s engines, and your call to
malloc()fails just after launch, the consequences could be much worse. Even if you can guarantee that there always be enough bytes of memory available for the worse-case scenario, you cannot guarantee that there will be enough in a contiguous block of memory, due to memory fragmentation (as memory is allocated and deallocated, it becomes fragmented).
The second reason is that dynamic allocation introduces a whole new category of bugs. You can now accidentally cause memory leaks. You can now forget to initialize the memory your just allocated before reading from it. You called
free()twice by accident and now have undefined behaviour.
The third reason is that memory allocation is non-deterministic. You do not know how long
malloc()is going to take to allocate you memory, which could be unacceptable for hard real-time applications.
C programming standards such as MISRA ban memory allocation outright.
Banning memory allocation does restrict the flexibility of the application (e.g. you can no longer have a data type which represents an arbitrary length string). However, the following workarounds are used in embedded applications:
- Static allocation. Chunks of memory can be allocated statically (at compile time). You typically allocate enough memory for your worst-case runtime scenario. For example, you could allocate
500 bytesfor your receive buffer, and because of the protocol used, you know that you will never need more than that.
- Memory pools. A memory pool is a block of memory that is allocated statically. The pool is divided up into pieces which can be “dynamically assigned” at runtime. This gives the the application most of the benefits of dynamic memory allocation but you always know how many blocks you have left in the pool, the request for memory is deterministic, and you do not suffer from fragmentation.
Overriding The Default Implementation Of malloc()
You can override the default, standard library provided implementations of
malloc() and friends just by specifying the functions (with the right input/output types) in your application code.
This work is licensed under a Creative Commons Attribution 4.0 International License .
- dynamic memory allocation
- memory pools
- static allocation
- memory leak