Project 4.01 Section 4 ⚡ Embedded Relevance: Critical C-Style Arrays Stack Memory Array Decay sizeof Trap Buffer Overflow

4.01 Fixed-Size C Arrays, Stack Allocation & Array Decay to Raw Pointers

Executive Summary: Exploring foundational C-style arrays on the CPU stack. We examine array declaration, zero-based indexing, how arrays implicitly decay into raw pointers when passed to functions (losing size metadata), and why stack buffer overflows are the #1 cause of embedded security exploits.

💻 1. Annotated Source Code

#include <iostream>
using namespace std;

int main() {

	const int ARRAY_SIZE = 5;
	int myArray[ARRAY_SIZE];  

	myArray[0] = 15;
	myArray[1] = 20;
	myArray[2] = 22;
	myArray[3] = 13;
	myArray[4] = 6;

	for (int i = 0; i <= ARRAY_SIZE; i++) {
		cout << myArray[i] << endl;
	}

	return 0;
}

📐 2. Architecture & UML Class Model

📐 Fixed Array Memory Contiguity & DMA Model
+ Public - Private # Protected
<<struct>> StaticArrayBuffer Contiguous SRAM Block
+data[5] : int32_t (20 bytes contiguous)
+size : constexpr size_t = 5
+at(idx: size_t) : int32_t&
+fill(val: int32_t) : void
+printElements() : void const

📚 3. Core C++ Concepts Deep-Dive

1. Contiguous Stack Allocation

A C-style array (int arr[5]) allocates a contiguous sequence of elements directly on the current stack frame. The variable name arr represents the starting memory address of the block.

2. The Array-to-Pointer Decay Trap

When passed to a function (void print(int arr[])), the array decays into a raw pointer (int*). sizeof(arr) inside the function returns the pointer size (4 or 8 bytes), NOT the total array size!

⚡ 4. Embedded Systems & Hardware Reality

1. Stack Buffer Overflows & Return Address Hijacking

In microcontrollers without virtual memory protection, writing beyond an array index corrupts the function's saved Link Register (LR) / Return Address on the stack frame. When the function returns, the CPU jumps to an arbitrary address, causing execution hijacking or HardFault crashes.

2. MISRA C++:2008 Rule 5-0-15

Array indexing shall be the only form of pointer arithmetic. Pointer decay when passing arrays across API boundaries is strongly discouraged in favor of std::array or bounded span wrappers.

💡 5. Production-Ready Embedded Refactoring

Replace raw decaying arrays with zero-overhead, size-preserving std::array:

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

// Type-safe, non-decaying array parameter
template <size_t N>
void processSamples(const std::array<uint16_t, N>& samples) noexcept {
    static_assert(N > 0, "Sample buffer cannot be empty");
    for (uint16_t val : samples) {
        // Process sensor sample...
    }
}

📝 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 happens when a C-style array is passed by value to a function (void func(int arr[]))?
A It decays into a raw pointer (int*), losing compile-time size information
B A deep copy of the entire array is pushed to the stack
C A compile-time syntax error is generated
D The array is moved into the heap
Detailed Explanation: In C and C++, arrays passed by name decay into a pointer to the first element (int*), discarding container size information.
Q2. Why is sizeof(arr) / sizeof(arr[0]) hazardous when used inside a function on a decayed array parameter?
A It computes sizeof(int*) / sizeof(int), yielding 1 on 32-bit MCUs regardless of actual array size
B It causes a hardware division-by-zero trap
C It dynamically allocates heap memory
D It changes the array elements to zero
Detailed Explanation: Because the array decayed to a pointer, sizeof(arr) evaluates to the pointer size (4 bytes), yielding 4 / 4 = 1.
Q3. How does a stack buffer overflow compromise microcontroller firmware?
A Writing past array boundaries overwrites the saved return address (LR) on the stack, crashing the CPU or hijacking execution flow
B It erases the EEPROM memory chips
C It lowers the microcontroller voltage supply
D It permanently disables the JTAG debugger
Detailed Explanation: The stack frame stores local variables adjacent to saved registers (LR/PC). Overwriting these registers diverts CPU execution to corrupted addresses.
Q4. What is the memory overhead of std::array<int, 10> compared to a raw int arr[10] array?
A Exactly 0 bytes; std::array is a zero-cost abstraction with identical memory layout
B 4 bytes for the size field
C 16 bytes for heap allocator pointers
D 24 bytes for virtual method tables
Detailed Explanation: std::array contains only the underlying C array internally with zero extra members, making its size and memory layout identical to a raw array.