Arrays & Vectors
Two Ways to Store a List
Imagine you're moving into a new apartment. You could stack your books directly on the floor in a neat row — that's a C-style array. You know exactly how many books you have, the row never grows, and if you accidentally step past the end, you crush something.
Or you could buy a bookshelf with expandable sections — that's a std::vector. It starts with some room, and when you run out, it quietly gets a bigger shelf and moves everything over. You barely notice.
C-Style Arrays — The Old Guard
C++ inherited raw arrays from C. They're fast and sit directly on the stack, but they come with serious trade-offs:
- Size is fixed at compile time
- No bounds checking — go past the end and you silently corrupt memory
- They decay to pointers when passed to functions, losing their size
For new code, you almost never need raw arrays. But you'll see them in legacy codebases and interview questions, so let's look at them briefly.
C-Style Array Basics
std::array — The Modern Fixed-Size Array
C++11 introduced std::array as a safe wrapper around C-style arrays. It knows its own size, supports iterators, and works with STL algorithms — all with zero overhead compared to a raw array.
Use std::array when you know the size at compile time and it won't change.
std::array — Fixed Size, Full Safety
std::vector — The Star of the Show
This is the container you'll use 90% of the time in C++. A vector is a dynamic array — it grows and shrinks as needed. Under the hood, it manages a chunk of heap memory and reallocates when it runs out of room.
Key Concepts: Size vs. Capacity
A vector has two numbers that matter:
- Size — how many elements are currently stored
- Capacity — how much memory is allocated (always ≥ size)
When you push_back and the size would exceed capacity, the vector allocates a new, larger block (typically 2x the old capacity), copies everything over, and frees the old block. This is why push_back is amortized O(1) — most calls are instant, but occasionally one triggers a reallocation.
Vector Operations — The Essentials
at() vs [] — Safety vs Speed
vec.at(i) during development — it throws an exception on out-of-bounds access. vec[i] is faster but silently corrupts memory if you go out of bounds. Switch to [] only in performance-critical paths after you've verified correctness.reserve() — Avoiding Unnecessary Reallocations
2D Vectors — Vector of Vectors
When to Use What?
- std::vector — default choice. Use it unless you have a specific reason not to.
- std::array — when the size is known at compile time and fixed (e.g., RGB color = 3 values).
- C-style arrays — almost never in new code. Only when interfacing with C libraries or in very low-level code.
If you reserve() ahead of time, vectors match raw arrays in performance for sequential access. The compiler is smart enough to optimize away the overhead.