Embedded C++ & Safety-Critical Engineering: MISRA, Freestanding, Placement New & std::expected

Embedded C++ & Safety-Critical Engineering: MISRA, Freestanding, Placement New & std::expected
Table of Contents
- Hosted vs Freestanding C++
- Why Dynamic Allocation is Forbidden in Safety-Critical Code
- Static Allocation with Placement New
- No Exceptions: std::expected as Deterministic Error Handling
- MISRA C++:2023 Key Rules
- Interrupt Service Routines in C++
- Fixed-Point Arithmetic
- C++23 Freestanding Additions
- Stack Usage Analysis and Worst-Case Execution Time
- Frequently Asked Questions
- Key Takeaway
Hosted vs Freestanding C++
Why Dynamic Allocation is Forbidden in Safety-Critical Code
In automotive (ISO 26262), aerospace (DO-178C), and medical (IEC 62304) standards, dynamic memory allocation after system initialization is typically prohibited because:
- Non-deterministic timing:
malloc/newexecution time depends on heap state — unpredictable in real-time systems - Fragmentation: Long-running systems with frequent alloc/free develop heap fragmentation — eventual
mallocfailure - Out-of-memory: Dynamic allocation can fail at runtime; static allocation fails at compile time (link-time)
- WCET analysis: Worst-Case Execution Time analysis is impossible when heap allocation time is unbounded
Static Allocation with Placement New
Placement new constructs an object at a pre-allocated memory location — combining static allocation with C++ object construction:
No Exceptions: std::expected as Deterministic Error Handling
The -fno-exceptions compiler flag disables C++ exception support entirely — no throw, no try/catch. Use std::expected<T, E> (C++23) for structured error propagation:
MISRA C++:2023 Key Rules
MISRA C++:2023 (Motor Industry Software Reliability Association) updated for modern C++:
| Rule | Category | Rationale |
|---|---|---|
| No dynamic heap allocation after init | Required | Non-deterministic, fragmentation |
No exceptions (-fno-exceptions) | Required | Non-deterministic unwind time |
| No multiple inheritance | Required | Diamond problem, layout complexity |
| No recursion without depth bound | Required | Stack overflow risk |
override on all overriding functions | Required | Prevent silent signature mismatch |
No goto | Required | Unstructured control flow |
| No raw pointer arithmetic | Advisory | Buffer overrun risk |
| Use RAII for all resources | Required | Deterministic release |
| All variables initialized at declaration | Required | Undefined behavior prevention |
No implicit conversions (use static_cast) | Advisory | Unintended narrowing |
Interrupt Service Routines in C++
Frequently Asked Questions
Is C++ better than C for embedded systems?
Yes, when used correctly. C++ templates provide zero-cost abstractions over what C achieves with macros. RAII guarantees register/peripheral cleanup even under error paths. std::array replaces unsafe C arrays. std::expected replaces error-code sprawl. The risk is misusing hosted-only features (std::vector, std::string, exceptions) — MISRA and static analysis tools catch this.
Can I use the STL in embedded code?
Selectively. The following are safe in embedded (no dynamic allocation): std::array, std::string_view, std::span, std::optional, std::expected, std::pair/tuple (with trivial types), std::variant, type traits, constexpr algorithms. The following require a heap and are typically forbidden: std::vector, std::string, std::map, std::list, std::function.
What is WCET analysis and how does it affect C++?
Worst-Case Execution Time (WCET) analysis determines the maximum time any code path can take — essential for real-time systems where tasks must complete within deadlines. Dynamic dispatch (virtual functions), heap allocation, and exception unwind defeat WCET because their execution time is data-dependent. CRTP (static polymorphism), static allocation, and std::expected are WCET-friendly.
Key Takeaway
Embedded C++ is not "less C++"; it's C++ with explicit constraints turned into engineering discipline. The constraints — no heap after init, no exceptions, deterministic timing — force you to think about resource usage at every level. The reward: code that runs correctly in a car's brake controller for 15 years, a pacemaker's pulse generator for 10 years, or a satellite's attitude control system indefinitely. Mastering these constraints makes you a better engineer for all C++ contexts.
Read next: Enterprise Deployment: CI/CD & Final Wrap-up →
Part of the C++ Mastery Course — 30 modules from modern C++ basics to expert systems engineering.
