Deterministic dynamic memory allocation & fragmentation in C & C++
In C and C++, it can be very convenient to allocate and de-allocate blocks of memory as and when needed. This is certainly standard practice in both languages and almost unavoidable in C++.
However, the handling of such dynamic memory can be problematic and inefficient. For desktop applications, where memory is freely available, these difficulties can be ignored. For embedded - generally real time - applications, ignoring the issue is not an option.
Dynamic memory allocation tends to be non-deterministic; the time taken to allocate memory may not be predictable and the memory pool may become fragmented, resulting in unexpected allocation failures. This article details the problems and an approach to deterministic dynamic memory allocation.
Dynamic Memory in C
In C, dynamic memory is allocated from the heap using some standard library functions. The two key dynamic memory functions are malloc() and free(). The malloc() function takes a single parameter, which is the size of the requested memory area in bytes. It returns a pointer to the allocated memory. If the allocation fails, it returns NULL. The prototype for the standard library function is like this:
void *malloc(size_t size);
The free() function takes the pointer returned by malloc() and de-allocates the memory. No indication of success or failure is returned. The function prototype is like this:
void free(void *pointer);
To illustrate the use of these functions, here is some code to statically define an array and set the fourth element's value:
my_array = 99;
The following code does the same job using dynamic memory allocation:
pointer = malloc(10 * sizeof(int));
*(pointer+3) = 99;
The pointer de-referencing syntax is hard to read, so normal array referencing syntax may be used, as [ and ] are just operators:
pointer = 99;
When the array is no longer needed, the memory may be de-allocated thus:
pointer = NULL;
Assigning NULL to the pointer is not compulsory, but is good practice, as it will cause an error to be generated if the pointer is erroneous utilized after the memory has been de-allocated.
The amount of heap space actually allocated by malloc() is normally one word larger than that requested. The additional word is used to hold the size of the allocation and is for later use by free(). This "size word" precedes the data area to which malloc() returns a pointer.
There are two other variants of the malloc() function: calloc() and realloc(). The calloc() function does basically the same job as malloc(), except that it takes two parameters " the number of array elements and the size of each element " instead of a single parameter (which is the product of these two values). The allocated memory is also initialized to zeros. Here is the prototype:
void *calloc(size_t nelements, size_t elementSize);
The realloc() function resizes a memory allocation previously made by malloc(). It takes as parameters a pointer to the memory area and the new size that is required. If the size is reduced, data may be lost. If the size is increased and the function is unable to extend the existing allocation, it will automatically allocate a new memory area and copy data across. In any case, it returns a pointer to the allocated memory. Here is the prototype:
void *realloc(void *pointer, size_t size);