Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
The TL;DR is: use the correct tool for the task. Dynamic memory allocation in the context of embedded microcontroller system is indeed pretty much never the correct tool, for a long list of reasons...
#1: Initial revision
The TL;DR is: use the correct tool for the task. Dynamic memory allocation in the context of embedded microcontroller system is indeed pretty much never the correct tool, for a [long list of reasons](https://electrical.codidact.com/posts/286121). Ask yourself what your actual task is - what is the reason you need dynamic memory? If the answer is something like "I need a shared memory pool" or "I don't know the memory required at compile-time", that's the wrong answer - _why_ do you need a shared memory pool, _why_ don't you know the upper limit of what your program is supposed to be doing? Keep asking _why_ until your answer lands in the specific project requirements. Or if it doesn't land there, then either the requirements are insufficient and should be revisited. Or it could turn out that you are doing some meta task unrelated to the actual project, in which case you should abandon it and get back on track. --- I believe that the code in the question comes from [Static allocation of opaque data types](https://stackoverflow.com/a/70667901/584518) and it's important to understand that context. If you _don't_ have any opaque types simply declare the object as a plain `static` file scope variable and don't worry about memory pools. In the specific scenario when you have an opaque type, achieved by forward declaring an incomplete `struct`, the application code simply cannot allocate an instance of that object since the `struct` implementation and size is unknown to the application. Therefore you have two options: - The opaque type class allocates memory for every object of that type internally, through a constructor-like function, or - The caller allocates memory in some global memory pool by asking the opaque type class how large one object is, through some manner of "get_size()" API. For simple applications the former is likely preferred as it makes things less complex. The latter is only preferable when you have a lot of opaque types and you want their memory allocated together (for example for data cache reasons). --- Notably one has to be aware that these memory pools that work on a character type array do declare an array with "effective `uint8_t` type". If we let a `struct` point into such an array and that struct does not have a similar `uint8_t` array among its members, then dereferencing the struct will explicitly invoke undefined behavior as per "strict aliasing". There are two ways to avoid it: either _don't_ use a `struct` but a `union` between the `struct` type and a `uint8_t` array of the same size as the struct. Or simply disable strict aliasing optimizations, which were only ever a problem with the gcc compiler specifically - the option is `gcc -fno-strict-aliasing`. --- > But one disadvantage is that if I have to "free" memory from processes that have finished using their memory block This doesn't make sense in embedded MCU systems and where most people considering dynamic allocation go wrong. The scenario "I don't know how much memory I need at compile-time" does not exist. A microcontroller system, or any high reliability system for that matter, must be fully deterministic and there can be no unknowns. From [Why should I not use dynamic memory allocation in embedded systems?](https://electrical.codidact.com/posts/286121): > **"Saving memory" and freeing doesn't make sense** > > Calling free() in a single core microcontroller application never makes any sense - there is nobody to share the memory with, our program has complete control of it all. As established earlier, we need to handle the worst case scenario so we need to allocate that exact amount. Freeing up memory when we are not executing the worst case scenario is senseless, because if there are parts of the code which would actually benefit from that extra available memory, that only means that those very same parts will either perform poorly or fail/crash during the worst case scenario, so that would be a design mistake. Your requirements and application use-cases must cover all situations including the worst-case ones. Your application must work just as fine in the worst-case scenario as in any other scenario, so you know in advance how much memory you need: exactly as much as is necessary for the worst-case scenario. Not more, not less - you need that exact amount. There is nothing "dynamic" about it. The need for dynamic memory is typically just one big "XY problem". You think you need solution "dynamic memory" to solve problem X, and so you are looking for various alternatives to dynamic memory when it's likely the wrong solution to your original problem to begin with. So consider: when exactly will any part of your program be "finished" with a memory block and how does that even make sense from your requirements' and program design's point of view? Did you pick a MCU with insufficient RAM for the project requirements or what? All explanations from there on enters the realm of dirty patches and ad hoc solutions, when we have thrown all good programming practices out the window anyway. If your processes have to wait for each other to finish just because there is too little memory, then clearly you have picked the wrong MCU to host the RTOS. And why are individual processes even mucking around with a chunk of shared memory instead of using their local process stack for it? (Ie the RTOS' equivalent to `thread_local`.) At this point we need to stop and question what we are even doing, because our program design is apparently all over the place and maybe we've over-engineered the whole project. --- > What are some other memory pool architectures/types than this arena type? There are various even more specialized types and which ones that apply might matter if the data is read/write or read-only. A linked list implemented on top of a static array with array indexes instead of pointers for example, which can be a handy type for implementing queues or even certain wear leveling algorithms. Hash tables could make sense if you are dealing with large amounts of data and need more or less constant look-up time. These make most sense when the data is read-only. Same with binary trees, expression parsing trees etc etc. They key is specialized use, just as the memory pool in the question is a container for specialized use.
