5.13 Local vs Global Scope, Static Variables (.data/.bss) & Reentrancy Hazards
Executive Summary: Analyzing variable scope and lifetime: local (automatic stack), global, and local static storage. We analyze the memory map placement of static variables (.data vs .bss) and the severe reentrancy hazards of static variables in multi-threaded RTOS tasks.
💻 1. Annotated Source Code
#include <iostream> using namespace std; void someFunction(int aParam); double myGlobalDouble = 3.1415; int main() { int localToMain = 20; cout << "The local to main variable is: " << localToMain << endl; cout << "The global double (in main) is: " << myGlobalDouble << endl; someFunction(25); someFunction(28); someFunction(32); //cout << myLocalNum << endl; return 0; } void someFunction(int aParam) { int myLocalNum = 100; myLocalNum++; static int myStatic = 500; myStatic++; myGlobalDouble++; cout << "My local number: " << myLocalNum << endl; cout << "The parameter is: " << aParam << endl; cout << "Global double (in someFunction) is: " << myGlobalDouble << endl; cout << "myStatic: " << myStatic << endl; //localToMain++; }
📐 2. Architecture & UML Class Model
<<compilation-unit>>
ScopeResolver
Lifetime Matrix
Attributes / Data Members
+globalVar : int32_t (.data section)
-staticVar : int32_t (.bss persistent)
Operations / Methods
+testScope() : void
+shadowDemo(globalVar: int) : void
📚 3. Core C++ Concepts Deep-Dive
1. Storage Duration Categories
- Automatic (Stack): Created at block entry, destroyed at block exit.
- Static (RAM): Allocated once at startup, persists for the entire program execution.
- Dynamic (Heap): Allocated via
new, persists untildelete.
⚡ 4. Embedded Systems & Hardware Reality
1. The Reentrancy Hazard of Local Static Variables
A function containing a local static variable (static int counter = 0;) is NOT reentrant. If an interrupt routine (ISR) preempts the function and calls it again, or if two RTOS tasks execute it concurrently, the static variable will suffer race conditions.
2. Memory Placement: .data vs .bss
Initialized static variables live in .data (copied from Flash to RAM at boot). Uninitialized static variables live in .bss (zeroed at boot).
💡 5. Production-Ready Embedded Refactoring
Reentrant task-safe function design:
💡 Production-Ready Refactor
#include <cstdint> // Reentrant: All state is passed via caller-provided context (Zero static state) struct CounterContext { uint32_t count{0}; }; uint32_t incrementReentrant(CounterContext& ctx) noexcept { return ++ctx.count; // 100% thread-safe across independent tasks }
📝 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 a 'reentrant function' in embedded systems?
Detailed Explanation:
Reentrant functions rely only on caller-provided stack data and avoid shared static/global state, allowing safe concurrent execution.
Q2. Why does a function containing a local 'static int count = 0;' fail reentrancy checks in an RTOS?
Detailed Explanation:
Local static variables share a single global memory location across all invocations, creating race conditions when called concurrently.
Q3. Where is an initialized global variable (int sensor_id = 42;) stored in the microcontroller memory map?
Detailed Explanation:
Initialized static/global variables reside in
.data; their initial values are stored in Flash and copied into RAM by startup code.
Q4. What is the lifetime of a local variable declared inside a function body?
Detailed Explanation:
Local variables have automatic storage duration, existing only while execution is inside their enclosing lexical block.