Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 2 of 9

std::array is a C array with the sharp edges removed. Same memory layout, same speed, but it knows its own size and doesn't decay.

#include <array>
#include <iostream>

int main() {
    std::array<int, 5> scores = {90, 85, 77, 100, 68};

    std::cout << scores[0] << " " << scores[4] << "\n";
    std::cout << scores.size() << "\n";
    std::cout << scores.front() << " " << scores.back() << "\n";
    return 0;
}
90 68
5
90 68

The declaration reads std::array<element type, count>. That's your first look at template arguments — the angle brackets say "an array of 5 ints" as one complete type. Module 12 explains how they work; for now, just read them.

It doesn't decay

The whole point:

#include <array>
#include <iostream>

void takes_array(const std::array<int, 5>& values) {
    std::cout << "inside:  " << values.size() << "\n";
}

int main() {
    std::array<int, 5> numbers = {1, 2, 3, 4, 5};
    std::cout << "outside: " << numbers.size() << "\n";
    takes_array(numbers);
    return 0;
}
outside: 5
inside:  5

Compare lesson 6.1, where the same thing printed 20 and then 8. The size travels with the object, so no separate count parameter is needed and there's nothing to get out of sync.

Note the const& — lesson 5.4's rule applies, since copying an array copies every element.

Bounds checking, when you ask for it

operator[] is unchecked, exactly like a C array. But .at() checks:

#include <array>
#include <iostream>
#include <stdexcept>

int main() {
    std::array<int, 3> values = {10, 20, 30};

    std::cout << values[1] << "\n";
    std::cout << values.at(1) << "\n";

    try {
        std::cout << values.at(10) << "\n";
    } catch (const std::out_of_range& e) {
        std::cout << "caught: out of range\n";
    }
    return 0;
}
20
20
caught: out of range

values[10] would be silent undefined behaviour. values.at(10) throws std::out_of_range, which you can catch (Module 15 covers exceptions properly).

The trade is a comparison per access. Use .at() when the index comes from outside your control — user input, a file, a network message — and [] in tight loops where you've already checked the bounds.

The useful members

#include <array>
#include <iostream>

int main() {
    std::array<int, 4> values = {5, 3, 9, 1};

    std::cout << values.size() << " " << values.empty() << "\n";
    std::cout << values.front() << " " << values.back() << "\n";

    values.fill(7);
    std::cout << values[0] << values[1] << values[2] << values[3] << "\n";
    return 0;
}
4 0
5 1
7777

There's also .data(), which hands you a raw pointer — exactly what you need when calling a C API that expects one.

Copying and comparing actually work

C arrays can't be assigned or compared with ==. std::array can:

#include <array>
#include <iostream>

int main() {
    std::array<int, 3> a = {1, 2, 3};
    std::array<int, 3> b = a;          // real copy
    std::array<int, 3> c = {1, 2, 3};

    b[0] = 99;

    std::cout << std::boolalpha;
    std::cout << (a == c) << " " << (a == b) << "\n";
    std::cout << a[0] << " " << b[0] << "\n";
    return 0;
}
true false
1 99

a == c compares element by element. With C arrays, == would compare addresses — always false for two distinct arrays, and a bug that compiles silently.

The size is part of the type

// WILL NOT COMPILE
#include <array>

void takes_five(const std::array<int, 5>& values) {
    (void)values;
}

int main() {
    std::array<int, 3> three = {1, 2, 3};
    takes_five(three);          // array<int,3> is not array<int,5>
    return 0;
}

std::array<int, 3> and std::array<int, 5> are entirely different types. That's good for safety and inconvenient when you want a function accepting any size — which needs a template (Module 12) or std::span (lesson 13.11).

std::array versus std::vector

| | std::array | std::vector | | --- | --- | --- | | Size | fixed at compile time | grows at run time | | Memory | on the stack | on the heap | | Cost | zero overhead | one allocation | | Use when | the count is known and small | the count varies or is large |

Prefer std::array when the size is genuinely fixed — a 3D coordinate, a chess board, a fixed lookup table. It's the cheapest correct option. When you don't know the count in advance, you need lesson 6.3.

Try it

Store the number of days in each month in a std::array<int, 12>, then write a function taking it by const& that returns the total. Use .at() to look up a month by a number the user might have got wrong.

Checkpoint

  • std::array<T, N> is a fixed-size array that carries its size with it.
  • It does not decay when passed to a function — pass it by const&.
  • [] is unchecked; .at() throws std::out_of_range. Use .at() for untrusted indices.
  • .size(), .front(), .back(), .fill(), .data() are the members you'll reach for.
  • Unlike C arrays, it can be copied with = and compared with == element-wise.
  • The size is part of the type, so array<int,3> and array<int,5> don't interchange.

Questions about this lesson

Log in to ask a question.