8.03 new / delete Lifecycles, Dangling Pointers & Heap Fragmentation in Microcontrollers
💻 1. Annotated Source Code
#include <iostream> using namespace std; int main() { //int* myIntPtr = new int(123); int* myIntPtr = new int; bool* myBoolPtr = new bool; *myBoolPtr = true; *myIntPtr = 123; cout << *myIntPtr << endl; cout << boolalpha; cout << *myBoolPtr << endl; delete myIntPtr; delete myBoolPtr; myIntPtr = nullptr; myBoolPtr = nullptr; return 0; }
📐 2. Architecture & UML Class Model
📚 3. Core C++ Concepts Deep-Dive
1. Heap Allocation with new and delete
The new operator requests memory from the free store (heap), invokes the object's constructor, and returns a pointer. The delete operator invokes the destructor and releases the memory back to the heap.
2. Dangling Pointers and Double-Free Bugs
After calling delete ptr;, the pointer variable still holds the original address (a dangling pointer). Dereferencing it is Undefined Behavior. Calling delete twice on the same pointer corrupts the heap metadata. Always set ptr = nullptr; immediately after deletion.
⚡ 4. Embedded Systems & Hardware Reality
1. Why Dynamic Heap Allocation is Banned in Embedded Safety Systems
- Heap Fragmentation: Repeated allocations and deallocations of varying sizes create tiny free holes across SRAM. Eventually, a small allocation request fails (
std::bad_alloc) even if total free RAM exceeds the requested size. - Non-Deterministic Latency: Heap allocators (
malloc) search free lists. The time required varies wildly based on fragmentation state, violating real-time deadlines. - Stack-Heap Collision: In bare-metal linker scripts, the stack grows downwards while the heap grows upwards. A heap overflow silently overwrites the active stack, corrupting return addresses.
🚫 MISRA C++:2008 Rule 18-0-1 & NASA C Style Guide
Dynamic memory allocation shall not be used after firmware startup initialization. All buffers and objects must be statically or stack allocated.
💡 5. Production-Ready Embedded Refactoring
Here is how embedded engineers replace heap new with static storage buffers (zero heap allocation):
#include <cstdint> #include <cstddef> #include <new> // Statically allocated memory slot (Zero Heap Overhead) template <typename T> class StaticSlot { alignas(T) std::byte storage_[sizeof(T)]; bool occupied_{false}; public: template <typename... Args> T* construct(Args&&... args) noexcept { if (occupied_) return nullptr; T* obj = new (storage_) T(std::forward<Args>(args)...); // Placement new occupied_ = true; return obj; } void destroy() noexcept { if (occupied_) { reinterpret_cast<T*>(storage_)->~T(); // Explicit destructor call occupied_ = false; } } };
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
nullptr is guaranteed to be a safe no-op in C++, and setting freed pointers to nullptr prevents accidental double-free and use-after-free bugs.