Project 8.03 Section 8 ⚡ Embedded Relevance: Critical new delete Heap Fragmentation Memory Leaks nullptr MISRA C++

8.03 new / delete Lifecycles, Dangling Pointers & Heap Fragmentation in Microcontrollers

Executive Summary: Analyzing dynamic memory allocation via new and delete, pointer resets to nullptr, and dangling pointer hazards. We explore why dynamic heap allocation is prohibited in high-reliability embedded systems (AUTOSAR / MISRA) due to heap fragmentation, non-deterministic latency, and catastrophic stack-heap collisions.

💻 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

📐 Heap Allocation (new/delete) vs Static Memory Pools
+ Public - Private # Protected
<<compilation-unit>> HeapManager Dynamic Allocator
-pDynamicInt : int32_t* (Allocated via new)
-pDynamicArray : double* (Allocated via new[])
+allocateHeap() : void
+deallocateHeap() : void[delete / delete[]]
<<embedded-pool>> StaticMemoryPool Zero-Fragmentation Pool
+poolBuffer[1024] : uint8_t (Fixed SRAM Block)
+allocatedOffset : size_t
+allocate(size: size_t) : void*
+reset() : void
🔗 Architectural Relationships & Hierarchy
HeapManager ─ ─ > refactors to ─ ─ > StaticMemoryPool

📚 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):

💡 Production-Ready Refactor
#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.

Q1. What is 'heap fragmentation' and why is it fatal in long-running embedded devices?
A Interspersed allocations and deallocations leave unusable gaps in SRAM, eventually causing allocation failure despite sufficient total free memory
B The CPU hardware clock frequency degrades over time
C The Flash ROM sectors wear out after 1000 writes
D The linker script fails to locate main()
Detailed Explanation: Heap fragmentation breaks contiguous free memory into small disjoint chunks. Over weeks or months of operation, memory requests fail because no single contiguous block is large enough.
Q2. What is a 'dangling pointer'?
A A pointer that still holds the memory address of an object that has already been deallocated/deleted
B A pointer that points to address 0x00000000
C A pointer stored inside an interrupt vector table
D A pointer passed to a function by reference
Detailed Explanation: A dangling pointer references memory that has been freed. Dereferencing or accessing a dangling pointer causes undefined behavior and data corruption.
Q3. What happens during a Stack-Heap collision in microcontroller SRAM?
A The downward-growing CPU stack and upward-growing heap overlap, causing silent data and return address corruption
B The compiler throws a std::stack_overflow exception
C The microcontroller enters low-power sleep mode
D The memory bus automatically expands into external flash
Detailed Explanation: In microcontrollers without an MMU/MPU guard band, the stack and heap grow toward each other. An overflow overwrites stack frames, causing unpredictable crashes.
Q4. Why is setting a pointer to nullptr after calling delete recommended?
A It prevents accidental use-after-free bugs and makes subsequent delete calls harmless (deleting nullptr is a safe no-op in C++)
B It physically erases the SRAM silicon cells
C It forces the compiler to inline the destructor
D It returns memory directly to the bootloader
Detailed Explanation: Deleting 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.