C++
STL Containers & Algorithms
Templates are what make this possible: the Standard Template Library (STL) is a set of ready-made, templated data structures and algorithms — a resizable array, a key-value map, sorting, searching — that would otherwise be exactly the kind of code the Embedded C domain shows you how to hand-roll from scratch, this time already written, tested, and generic over whatever type you need.
- What it is
- A collection of template classes (containers) and template functions (algorithms) forming C++'s standard data-structure toolkit.
- Mental model
- A well-stocked toolbox that ships with the language — before reaching for a hand-rolled linked list or array, check whether the STL already has exactly the tool you need.
- Key numbers
std::vectoris the default "just give me a resizable array" choice in the overwhelming majority of real C++ code — reach for anything else only when you have a specific reason to.- Reach for it when
- Almost always, on a target with enough memory to afford it — the STL is heavily used, well-tested, and removes real, repeated boilerplate versus hand-rolling equivalents.
std::vector
std::vector is a dynamically-resizable array — the direct, safer upgrade from
C's fixed-size arrays, growing automatically as elements are added:
#include <vector>
std::vector<int> readings;
readings.push_back(23);
readings.push_back(45);
readings.push_back(12);
std::cout << readings.size() << std::endl; // 3 — tracked automatically
std::cout << readings[1] << std::endl; // 45 — indexing works just like a C array
Unlike a C array, readings.size() is always known — no separate length variable to keep in
sync by hand, and no risk of the array/length pair drifting out of sync, a real recurring source of bugs
in equivalent C code.
Other common containers
| Container | What it is | Reach for it when |
|---|---|---|
std::vector | Dynamically-resizable array | The default choice — fast indexed access, grows as needed |
std::map | Sorted key-value pairs | Looking values up by a key, and iteration order (sorted by key) matters |
std::unordered_map | Hash-table key-value pairs | Looking values up by a key, and order doesn't matter — faster lookup than map |
std::set | Sorted, unique values | Needing a collection with no duplicates, automatically sorted |
std::queue | First-in-first-out | The STL version of C's hand-rolled queues |
#include <map>
std::map<std::string, float> sensorReadings;
sensorReadings["temperature"] = 23.5f;
sensorReadings["humidity"] = 45.0f;
std::cout << sensorReadings["temperature"] << std::endl; // 23.5 — looked up by name, not by index
The algorithms library
<algorithm> provides generic, templated functions that work across every STL
container — sorting, searching, and transforming, without writing the loop by hand:
#include <algorithm>
#include <vector>
std::vector<int> readings = {45, 12, 78, 23, 5};
std::sort(readings.begin(), readings.end());
// readings is now {5, 12, 23, 45, 78}
auto it = std::find(readings.begin(), readings.end(), 23);
if (it != readings.end()) {
std::cout << "Found 23\n";
}
begin()/end() are iterators — a generic "position within a
container" concept every STL container provides, letting the same std::sort function work
identically across a vector, a set, or any other compatible container without
being rewritten per container type.
Range-based for loops
A cleaner alternative to the index-based C-style for loop
when you just need every element, in order, without caring about the index itself:
for (int reading : readings) {
std::cout << reading << std::endl; // no index variable, no bounds to get wrong
}
Real-world applications
- Dynamic sensor buffers — a
std::vectorholding a growing log of readings, replacing a manually-managed, fixed-size C array plus a separate count variable. - Configuration lookups — a
std::map<std::string, std::string>holding parsed configuration key-value pairs, looked up by name. - Deduplication and sorted reporting — a
std::setcollecting unique device IDs seen on a network, automatically sorted for display.
Gotchas
- STL containers manage memory dynamically, using heap allocation under the hood — the same reasons Embedded C's Dynamic Memory gives for avoiding
mallocon a tiny, real-time-constrained target apply directly here too; heavy STL use is common on Linux-class embedded systems, much rarer on bare-metal microcontrollers. - Indexing a
std::vectorout of bounds with[]is undefined behavior, exactly like a raw C array —.at()is a slower but bounds-checked alternative that throws an exception instead. - A reference or iterator into a
std::vectorcan be invalidated by a laterpush_backthat triggers a resize (the vector's internal storage may move to a new location) — holding onto one across a modification is a real, easy-to-miss bug. std::mapandstd::setare typically implemented as balanced trees, with slower lookup than a hash-basedunordered_map/unordered_set— pick based on whether sorted iteration order actually matters for your use case.