Arrays, std::array & std::vector

C++ · Lesson 11 of 32 6 min read Updated

A class register has forty students, a week has seven temperatures, and a shopping cart has however many items the customer adds. Storing each value in its own variable would be impossible. C++ gives you three ways to store a sequence of values of the same type: the old C-style array, the fixed-size std::array and the growable std::vector.

In this lesson you will learn all three, see why modern code prefers std::vector and std::array, and finish with 2D grids and std::span.

C-style arrays

A C-style array has a fixed size that must be known at compile time. Items sit next to each other in memory and are numbered from 0.

C++
#include <iterator>
#include <print>

int main() {
    int marks[5] = {78, 92, 65, 88, 71};

    int total = 0;
    for (int m : marks) {
        total += m;
    }

    std::println("Subjects: {}", std::size(marks));
    std::println("Total: {}", total);
    std::println("First: {}, last: {}", marks[0], marks[4]);
}
Output
Subjects: 5
Total: 394
First: 78, last: 71

C-style arrays are fast but have serious weaknesses. They do not check indexes: marks[5] or marks[-1] silently read memory that belongs to something else. They cannot be copied with = or returned from a function. And when you pass one to a function it decays into a pointer to its first item, so the function loses the size. You will still meet them in older code and exams, but prefer the two standard containers below.

std::array: a fixed-size array that knows its size

std::array (header <array>, C++11) has the same speed as a C array but behaves like a normal value: it knows its size, can be copied and returned, and offers a checked at() function.

C++
#include <array>
#include <print>

int main() {
    std::array<int, 7> temps = {31, 33, 35, 34, 30, 29, 32};

    int hottest = temps[0];
    for (int t : temps) {
        if (t > hottest) {
            hottest = t;
        }
    }
    std::println("Days: {}, hottest: {} C", temps.size(), hottest);

    auto copy = temps;            // a real copy of all 7 values
    copy[0] = 0;
    std::println("Original: {}, copy: {}", temps[0], copy[0]);
    std::println("Day 3 with at(): {}", temps.at(2));
}
Output
Days: 7, hottest: 35 C
Original: 31, copy: 0
Day 3 with at(): 35

The type std::array<int, 7> holds the item type and the size. temps.at(7) would not read random memory; it throws a std::out_of_range exception, which stops the program with a clear message unless you handle it.

std::vector: the everyday container

std::vector (header <vector>) is a dynamic array: it grows and shrinks while the program runs. If you are unsure which container to use, use std::vector.

C++
#include <print>
#include <string>
#include <vector>

int main() {
    std::vector<std::string> cart;          // starts empty
    cart.push_back("Notebook");
    cart.push_back("Pen");
    cart.push_back("Geometry box");
    std::println("Items: {}", cart.size());

    cart.insert(cart.begin() + 1, "Eraser"); // Notebook, Eraser, Pen, Geometry box
    cart.pop_back();                         // removes Geometry box
    cart.erase(cart.begin());                // removes Notebook

    for (std::size_t i = 0; i < cart.size(); ++i) {
        std::println("{}: {}", i + 1, cart[i]);
    }
    std::println("Front: {}, back: {}", cart.front(), cart.back());
    std::println("Empty? {}", cart.empty());
}
Output
Items: 3
1: Eraser
2: Pen
Front: Eraser, back: Pen
Empty? false

The most useful vector functions:

FunctionWhat it does
push_back(x)Adds x at the end
pop_back()Removes the last item
insert(pos, x)Inserts x before position pos
erase(pos)Removes the item at pos
size(), empty()Number of items, and whether there are none
front(), back()First and last item
clear()Removes everything
reserve(n)Makes room for n items in advance

Positions for insert and erase are iterators, such as cart.begin() + 1 for "position 1"; you will learn more in Iterators & Algorithms. Adding at the end is fast. Inserting or erasing near the front is slower, because every later item must move along by one.

Removing values and building vectors

C++20 added std::erase, which removes every item equal to a value, in one call. You can also create a vector of a given size filled with one value.

C++
#include <print>
#include <vector>

int main() {
    std::vector<int> prices = {120, 45, 999, 15, 999, 300};
    std::erase(prices, 999);                 // remove every 999 (C++20)

    for (int p : prices) {
        std::print("{} ", p);
    }
    std::println("");

    std::vector<int> scores(4, 10);          // four items, each 10
    scores.push_back(25);
    std::println("Count: {}, last: {}", scores.size(), scores.back());
}
Output
120 45 15 300
Count: 5, last: 25

Note the brackets in std::vector<int> scores(4, 10): four items of value 10. With braces, std::vector<int> scores{4, 10} would create two items, 4 and 10. This is one place where braces and brackets mean different things.

2D grids: vectors of vectors

A table, a chess board or a cinema hall is a grid of rows and columns. A vector whose items are vectors models that well.

C++
#include <print>
#include <vector>

int main() {
    std::vector<std::vector<char>> seats(3, std::vector<char>(4, '.'));
    seats[1][2] = 'X';                       // row 1, seat 2 is booked
    seats[0][0] = 'X';

    for (const auto& row : seats) {
        for (char seat : row) {
            std::print("{} ", seat);
        }
        std::println("");
    }
}
Output
X . . .
. . X .
. . . .

seats[1][2] first picks row 1 (the second row), then seat 2 in that row.

Advanced: one function for every container with std::span

A function that takes const std::vector<int>& cannot accept a C array or a std::array. std::span (header <span>, C++20) is a lightweight view of any contiguous sequence, so one function works with all three:

C++
#include <array>
#include <print>
#include <span>
#include <vector>

int sum(std::span<const int> values) {
    int total = 0;
    for (int v : values) {
        total += v;
    }
    return total;
}

int main() {
    int raw[] = {1, 2, 3};
    std::array<int, 2> fixed = {10, 20};
    std::vector<int> growing = {100, 200, 300};

    std::println("{} {} {}", sum(raw), sum(fixed), sum(growing));
}
Output
6 30 600

Like std::string_view, a span does not own the data, so it must not outlive the container it looks at.

Common mistakes

  • Reading past the end. Valid indexes are 0 to size() - 1. Use at() while learning to catch mistakes.
  • Using [] on an empty vector. std::vector<int> v; v[0] = 5; is undefined behaviour; use push_back.
  • Mixing up (4, 10) and {4, 10}. Brackets mean "4 copies of 10", braces mean "the items 4 and 10".
  • Passing vectors by value. void show(std::vector<int> v) copies every item; use const std::vector<int>& or std::span<const int>.

Key points

  • C-style arrays are fixed, unchecked and decay to pointers; prefer standard containers.
  • std::array<T, N> is a fixed-size array that knows its size and can be copied.
  • std::vector<T> grows and shrinks with push_back, insert, erase and pop_back; it is the default container.
  • std::erase(v, value) (C++20) removes every matching item.
  • A vector of vectors models a 2D grid; std::span lets one function accept any contiguous sequence.

Exercise

Create a std::vector<int> with the runs Farhan scored in his last six matches: 34, 0, 78, 12, 0, 55. Remove all ducks (0) with std::erase, add a new score of 91, and then print the number of innings left, the total runs, and the highest score.

Show solution
C++
#include <print>
#include <vector>

int main() {
    std::vector<int> runs = {34, 0, 78, 12, 0, 55};
    std::erase(runs, 0);
    runs.push_back(91);

    int total = 0;
    int best = runs.front();
    for (int r : runs) {
        total += r;
        if (r > best) {
            best = r;
        }
    }

    std::println("Innings: {}", runs.size());
    std::println("Total: {}", total);
    std::println("Highest: {}", best);
}
Output
Innings: 5
Total: 270
Highest: 91

After removing the two ducks, four scores remain; adding 91 makes five. The total is 34 + 78 + 12 + 55 + 91 = 270.

Quick Quiz

1. What happens with int a[3] = {1, 2, 3}; int x = a[3];?

2. Which container should you use for a list whose size changes while the program runs?

3. What does std::vector<int> v(3, 7); create?

4. What does v.at(10) do on a vector with 4 items?

Learn C++ with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call