Arrays and std::array

An array holds a fixed number of elements of one type, stored side by side. C++ has the built-in C-style array and its modern replacement, std::array, which knows its own size, can be copied and compared, and works with the standard algorithms.

std::array

C++
#include <array>
#include <iostream>

int main() {
    std::array<int, 5> scores{72, 85, 90, 64, 88};
    std::cout << "first " << scores[0] << ", last " << scores.back() << ", size " << scores.size() << '\n';

    scores[3] = 70;
    int total = 0;
    for (int s : scores) total += s;
    std::cout << "average " << total / static_cast<double>(scores.size()) << '\n';
}
Output
first 72, last 88, size 5
average 81

The size is part of the type — std::array<int, 5> — and must be a compile-time constant. Elements you don't initialise are zero when you use braces: std::array<int, 5> a{};.

Bounds checking

a[i] is fast and unchecked: an index outside the array is undefined behaviour. a.at(i) checks and throws std::out_of_range instead:

C++
#include <array>
#include <iostream>
#include <stdexcept>

int main() {
    std::array<int, 3> data{1, 2, 3};
    try {
        std::cout << data.at(3) << '\n';
    } catch (const std::out_of_range& e) {
        std::cout << "caught: index 3 is out of range\n";
    }
}
Output
caught: index 3 is out of range

Many standard libraries can also check [] in debug builds (for example with -D_GLIBCXX_ASSERTIONS for GCC's library), which catches bugs during testing.

Arrays are values

Unlike C arrays, std::array objects can be copied, assigned, compared and returned from functions:

C++
#include <array>
#include <iostream>

std::array<int, 3> reversed(std::array<int, 3> a) {
    return {a[2], a[1], a[0]};
}

int main() {
    std::array<int, 3> a{1, 2, 3};
    auto b = a;                      // a full copy
    b[0] = 99;
    std::cout << a[0] << ' ' << b[0] << '\n';
    std::cout << std::boolalpha << (reversed(reversed(a)) == a) << '\n';
}
Output
1 99
true

Algorithms work on arrays

C++
#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>

int main() {
    std::array<int, 6> v{4, 17, -3, 8, 17, 2};
    std::sort(v.begin(), v.end());
    for (int x : v) std::cout << x << ' ';
    std::cout << "\nsum " << std::accumulate(v.begin(), v.end(), 0)
              << ", max " << *std::max_element(v.begin(), v.end())
              << ", count of 17: " << std::count(v.begin(), v.end(), 17) << '\n';
}
Output
-3 2 4 8 17 17
sum 45, max 17, count of 17: 2

Lesson 25 covers the algorithms library properly.

2D arrays

C++
#include <array>
#include <iostream>

int main() {
    std::array<std::array<char, 3>, 3> board{{
        {'X', 'O', '.'},
        {'.', 'X', 'O'},
        {'.', '.', 'X'},
    }};
    for (const auto& row : board) {
        for (char cell : row) std::cout << cell << ' ';
        std::cout << '\n';
    }
}
Output
X O .
. X O
. . X

C-style arrays

You'll meet built-in arrays in older code and C libraries. They work, but they don't know their size once passed to a function (they decay to a pointer), can't be assigned, and don't check bounds:

C++
#include <iostream>
#include <iterator>

int main() {
    int primes[] = {2, 3, 5, 7, 11};
    std::cout << std::size(primes) << " primes, last " << primes[std::size(primes) - 1] << '\n';
}
Output
5 primes, last 11

std::size works on both kinds of array. In new code, prefer std::array for fixed sizes and std::vector (next lesson) when the size can change.

Try it yourself

Count how often each die face appears using a std::array of counters.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Output
1: ##
2: #
3: ####
4: #
5: #
6: ###