Project 7.06 Section 7 ⚡ Embedded Relevance: Core std::logic_error std::out_of_range Bounds Checking Buffer Overflow static_assert

7.06 std::out_of_range vs Compile-Time Bounded Ranges in Memory-Constrained Systems

Executive Summary: Analyzing std::logic_error and std::out_of_range exceptions in C++. We explore how out-of-bounds memory accesses corrupt adjacent variables or trigger MPU faults in bare-metal systems, and design zero-overhead compile-time bounded types.

💻 1. Annotated Source Code

#include <iostream>
#include <vector>
#include <stdexcept> 

using namespace std;

int main() {

	vector<int> myNums;

	try {
		myNums.resize(myNums.max_size() + 1);
	}
	catch (const length_error& err) {
		cerr << "Caught a length_error: " << err.what() << endl;
	}


	cout << "Yay it's a big vector!" << endl;

	return 0;
}

📐 2. Architecture & UML Class Model

📐 std::logic_error & Invariant Verification Model
+ Public - Private # Protected
<<class>> std::logic_error Standard Logic Exception
-_M_msg : std::string
+logic_error(msg: const string&)
+what() : const char*[override, noexcept]
<<class>> std::length_error Derived Logic Exception
(none / stateless)
+length_error(msg: const string&)
+what() : const char*[override, noexcept]
🔗 Architectural Relationships & Hierarchy
std::length_error ──▷ inherits ──▷ std::logic_error

📚 3. Core C++ Concepts Deep-Dive

1. Logic Errors vs Runtime Errors

std::logic_error indicates violations of logical preconditions that could theoretically be detected by examining the program source code (e.g. passing an index $\ge$ size to std::vector::at()).

2. Subclasses of std::logic_error

  • std::out_of_range: Accessing elements outside valid container boundaries.
  • std::invalid_argument: Passing an improper argument to a function.
  • std::length_error: Attempting to create an object exceeding max_size.

⚡ 4. Embedded Systems & Hardware Reality

1. Out-of-Bounds Memory Corruption in Bare-Metal Systems

In standard C/C++, raw arrays (arr[i]) do not perform bounds checking. Writing past an array in embedded SRAM typically clobbers:

  • Adjacent global or local variables.
  • The function's Return Address on the stack, causing unpredictable jumps and HardFaults.
  • Interrupt Vector Tables in SRAM (triggering catastrophic execution hijacking).

2. Bounded Index Types (Zero-Cost Safety)

Instead of throwing std::out_of_range at runtime, embedded engineers use clamped/saturating integer arithmetic or strongly-typed bounded index wrappers.

💡 5. Production-Ready Embedded Refactoring

Here is a compile-time bounded array index that prevents out-of-bounds bugs at compile time:

💡 Production-Ready Refactor
#include <cstdint>
#include <cstddef>
#include <array>

template <typename T, size_t N>
class SafeArray {
    std::array<T, N> data_{};

public:
    // 1. Clamped access: Guarantees no out-of-bounds without throwing
    constexpr const T& at_clamped(size_t index) const noexcept {
        if (index >= N) index = N - 1;
        return data_[index];
    }

    // 2. Compile-time checked access for constant indices
    template <size_t Index>
    constexpr const T& get() const noexcept {
        static_assert(Index < N, "Array index is out of compile-time bounds!");
        return data_[Index];
    }
};

📝 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 the key conceptual difference between std::logic_error and std::runtime_error?
A logic_error represents preventable design bugs in code logic, while runtime_error represents unpredictable external failures
B logic_error only works with integers
C runtime_error is evaluated at compile time
D logic_error cannot be caught by a base class reference
Detailed Explanation: std::logic_error reflects flaws in the program's internal reasoning (like violating function preconditions), whereas std::runtime_error reflects environment/hardware conditions outside the program's control.
Q2. What happens in C++ if you access an invalid index using the subscript operator (arr[index]) on a raw array?
A Undefined Behavior (UB) occurs; memory is read or overwritten without any bounds check
B A std::out_of_range exception is automatically thrown
C The program safely returns null
D The array dynamically expands to fit the index
Detailed Explanation: Raw array subscripting ([]) in C and C++ performs direct pointer arithmetic with zero bounds checking. Accessing invalid indices causes undefined behavior and potential memory corruption.
Q3. Which method on std::vector performs bounds checking and throws std::out_of_range on invalid access?
A .at(index)
B [index]
C .front()
D .data()
Detailed Explanation: std::vector::at() checks whether the index is within the container bounds and throws std::out_of_range if it is not.
Q4. How does saturating/clamping arithmetic protect embedded sensor arrays from crashing?
A It clamps out-of-range index values to the nearest valid min/max boundary instead of overflowing
B It deletes corrupted elements from flash
C It restarts the microcontroller on every access
D It encrypts the index in CPU registers
Detailed Explanation: Clamping ensures that invalid indices or arithmetic results saturate at the nearest valid boundary (e.g. max_index), preventing out-of-bounds buffer corruptions.