7. Arrays & Vectors¶
What & why¶
Most programs work with collections: a list of scores, a row of pixels, lines from a file. C++'s workhorse collection is std::vector — a resizable array that stores its elements contiguously in memory. For a fixed number of elements known at compile time there's std::array. Old C arrays (int a[5]) exist too; you'll recognize them, but prefer the standard types.
std::vector¶
#include <vector>
std::vector<int> empty; // size 0
std::vector<int> scores{90, 72, 85}; // three elements
std::vector<double> zeros(5); // five 0.0s (parentheses = count)
std::vector<std::string> names(3, "?"); // three "?"s
std::vector<int> a(3); // {0, 0, 0} — size 3
std::vector<int> b{3}; // {3} — one element! Braces mean "these elements"
Access¶
scores[0] // 90 — fast, NO bounds check
scores.at(0) // 90 — bounds-checked, throws std::out_of_range
scores.front() // 90
scores.back() // 85
scores.size() // 3
scores.empty() // false
Adding and removing¶
scores.push_back(100); // append → {90, 72, 85, 100}
scores.pop_back(); // remove last → {90, 72, 85}
scores.insert(scores.begin() + 1, 60); // insert at index 1 → {90, 60, 72, 85}
scores.erase(scores.begin()); // remove index 0 → {60, 72, 85}
scores.clear(); // {}
push_back / pop_back at the end are fast. insert / erase in the middle shift every later element — slow for big vectors.
Iterating¶
for (const auto& s : scores) {
std::cout << s << ' ';
}
for (std::size_t i = 0; i < scores.size(); ++i) {
std::cout << i << ": " << scores[i] << '\n';
}
Iterators in one paragraph¶
v.begin() points at the first element; v.end() points one past the last. Standard algorithms take a range as a pair of iterators [begin, end). You'll learn them properly in the intermediate track; for now, think of them as "positions".
Sorting and searching with <algorithm>¶
#include <algorithm>
#include <numeric>
std::vector<int> v{5, 2, 9, 1, 7};
std::sort(v.begin(), v.end()); // {1, 2, 5, 7, 9}
std::sort(v.begin(), v.end(), std::greater<>{}); // {9, 7, 5, 2, 1}
std::reverse(v.begin(), v.end());
auto it = std::find(v.begin(), v.end(), 7);
if (it != v.end()) {
std::cout << "found 7 at index " << (it - v.begin()) << '\n';
}
int total = std::accumulate(v.begin(), v.end(), 0); // sum
auto biggest = *std::max_element(v.begin(), v.end());
auto n_even = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
C++20 ranges let you skip the begin()/end() pairs:
(The [](int x) { ... } is a lambda — an inline function. Full coverage in the intermediate track.)
Copying vs. referencing¶
Assigning a vector copies every element:
std::vector<int> a{1, 2, 3};
std::vector<int> b = a; // full, independent copy
b[0] = 99;
// a is still {1, 2, 3}
A reference is another name for the same object:
This matters most for function parameters (lesson 9): pass vectors as const std::vector<int>& to avoid copying.
std::array: fixed size¶
#include <array>
std::array<int, 3> rgb{255, 128, 0};
rgb.size(); // 3 — known at compile time
rgb[1] = 64;
std::ranges::sort(rgb);
Same interface as vector minus the resizing. It lives on the stack — no heap allocation.
C-style arrays (for recognition)¶
They don't know their own size, decay to pointers when passed to functions, and can't be copied with =. You'll meet them in older code and C APIs. Prefer std::array or std::vector.
2D data¶
std::vector<std::vector<int>> grid(3, std::vector<int>(4, 0)); // 3 rows × 4 cols
grid[1][2] = 7;
for (const auto& row : grid) {
for (int cell : row) {
std::cout << cell << ' ';
}
std::cout << '\n';
}
Structured bindings for pairs¶
std::pair holds two values; C++17 structured bindings unpack them:
#include <utility>
std::pair<std::string, int> entry{"Ada", 36};
auto [name, age] = entry;
std::cout << name << " is " << age << '\n';
Common mistakes¶
- Out-of-bounds
v[i]— undefined behavior, not an exception. Use.at()while learning, and compile with-D_GLIBCXX_DEBUG(GCC) to catch it. std::vector<int> v{5};expecting five zeros — it's one element,5. Usev(5).- Accidental copies —
auto copy = big_vector;or passing by value to functions. - Holding a reference/iterator across
push_back— the vector may reallocate, leaving it dangling. v.size() - 1on an empty vector —size()is unsigned, so this is a huge number.- Erasing while iterating with a range-
for— usestd::erase_if(v, pred)(C++20) instead.
Exercises¶
- Read integers until end-of-input into a vector, then print them sorted, the median, and the average.
- Remove all negative numbers from a vector using
std::erase_if. - Write a loop that reverses a vector in place by swapping elements (
std::swap(v[i], v[j])) — don't usestd::reverse. - Given
std::vector<std::string>, find the longest word. - Create a 10×10 multiplication table in a
std::vector<std::vector<int>>and print it aligned withstd::format("{:4}", x). - Demonstrate the reallocation trap: take
int& first = v[0];,push_back1000 elements, then printfirst. Compile with-fsanitize=addressand read the report.
Recap¶
std::vector<T>is the default container: contiguous, growable, fast at the end.v[i]is unchecked,v.at(i)is checked.size(),push_back,pop_back,erase.<algorithm>(sort,find,count_if, ...) and<numeric>(accumulate) work on iterator ranges;std::ranges::versions take the container directly.=copies;&references. Pass containers byconst&.std::array<T, N>for fixed-size data; avoid raw C arrays.
Next: Maps & sets →