C++ · Beginner

Arrays in C++

10 min readUpdated September 24, 2026Every example verified

In short: A C++ array is a fixed number of values of one type stored side by side, declared as int scores[5]; and indexed from 0 to size - 1. Its size is set at compile time and never checked at run time, so std::array<int, 5> (C++11) is the safer choice when the size is known and std::vector when it is not.

Many values under one name

An array holds several values of the same type in one block of memory. int visitors[5]; reserves room for five ints; visitors[0] is the first and visitors[4] the last. Indexes start at 0 because an index is an offset from the start of the block, and the first element is zero elements in. The index in brackets can be any integer expression, which is what makes arrays work with loops: for (int i = 0; i < 5; ++i) visits every element exactly once.

The size must be a constant the compiler can evaluate, such as a literal or a const int. Reading a size from input and writing int a[n] is accepted by g++ as an extension but is not standard C++; when the size is only known at run time, use std::vector, covered in the vectors lesson. An array can be initialised with a brace list, int stops[4] = {3, 8, 12, 15};, and the size may then be omitted, since the compiler counts the values. int counts[26] = {}; sets every element to zero. Without an initialiser, a local array holds garbage, exactly like an uninitialised int.

C++ does not check indexes. visitors[5] on a five-element array compiles and runs, reading or overwriting whatever happens to sit beyond the array; that is undefined behaviour and may work by accident today and crash tomorrow. Keeping the loop bound tied to the same constant as the declaration is the main defence.

The plain array is inherited from C and has awkward corners: it cannot be copied with =, it does not know its own size, and when passed to a function it turns into a pointer to its first element, so the function must be told the length separately. C++11 added std::array<T, N> from <array> to fix these. It has the same fixed size and the same speed, but it can be copied, it reports .size(), .at(i) throws an exception instead of silently misbehaving on a bad index, and it works cleanly with the range-based for loop and the algorithms you will meet later. Prefer it over a raw array in new code.

The range-based for, for (int minute : stops), walks over every element in order and gives you the value without an index. Declare the loop variable as a reference, for (int& x : arr), when you need to modify the elements in place.

A two-dimensional array is an array of arrays: char seats[3][4] is three rows of four chars, indexed as seats[row][col]. Nested loops visit the grid row by row. Both dimensions must be constants, and the brace initialiser nests one list per row.

Syntax

 C++ · syntax
type name[SIZE];                     // SIZE is a compile-time constant; contents indeterminate
type name[SIZE] = {v0, v1, v2};      // remaining elements become 0
type name[] = {v0, v1, v2};          // size deduced: 3
type name[SIZE] = {};                // every element zero

name[i]                              // element i, from 0 to SIZE - 1; not bounds-checked

#include <array>
std::array<type, SIZE> name = {v0, v1, v2};
name.size()                          // SIZE
name[i]                              // unchecked, like a plain array
name.at(i)                           // throws std::out_of_range on a bad index

for (type value : name) { ... }      // range-based for, one pass per element
for (type& value : name) { ... }     // by reference: assignments change the array

type grid[ROWS][COLS];               // two-dimensional
grid[r][c]

std::vector is the growable alternative; use it whenever the number of elements is not fixed at compile time.

Filling an array from input and scanning it

Five daily visitor counts are stored, then totalled and searched for the busiest day.

 C++
#include <iostream>

int main() {
    const int DAYS = 5;
    int visitors[DAYS];              // five ints, not yet given values
    for (int i = 0; i < DAYS; ++i) {
        std::cin >> visitors[i];
    }

    int total = 0;
    int busiest = 0;                 // index of the busiest day so far
    for (int i = 0; i < DAYS; ++i) {
        total += visitors[i];
        if (visitors[i] > visitors[busiest]) {
            busiest = i;
        }
    }
    std::cout << "Total: " << total << "\n";
    std::cout << "Average: " << static_cast<double>(total) / DAYS << "\n";
    std::cout << "Busiest day: " << busiest + 1 << " with " << visitors[busiest] << "\n";
    std::cout << "First and last: " << visitors[0] << " and " << visitors[DAYS - 1] << "\n";
    return 0;
}

Input given to the program: 132 98 210 175 210

Output

Total: 825
Average: 165
Busiest day: 3 with 210
First and last: 132 and 210

The constant DAYS sizes the array and bounds both loops, so the three can never disagree. The first loop reads straight into each element; the second keeps the index of the largest value seen so far rather than the value itself, so it can report which day it was. The strict > means the first of two equal maximums is kept, which is why day 3 wins over day 5. DAYS - 1 is the last valid index.

std::array with size(), range-for and at()

A bus timetable of minutes past the hour is walked forwards with range-for and backwards with an index.

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

int main() {
    std::array<int, 6> stops = {3, 8, 12, 15, 21, 27};   // minutes past the hour
    std::cout << "Stops: " << stops.size() << "\n";

    int previous = 0;
    for (int minute : stops) {
        std::cout << "Gap: " << minute - previous << "\n";
        previous = minute;
    }

    std::cout << "Reversed:";
    for (int i = stops.size() - 1; i >= 0; --i) {
        std::cout << " " << stops[i];
    }
    std::cout << "\n";

    stops[0] = 5;
    std::cout << "First stop now " << stops.at(0) << "\n";
    return 0;
}

Output

Stops: 6
Gap: 3
Gap: 5
Gap: 4
Gap: 3
Gap: 6
Gap: 6
Reversed: 27 21 15 12 8 3
First stop now 5

stops.size() is known to the object, so the loops do not need a separate constant. The range-for gives each minute in turn; the code keeps the previous one to print the gap. The reverse loop uses an int index so that i >= 0 can become false; with an unsigned counter it never would. at(0) does the same as stops[0] here but would throw std::out_of_range for an index of 6 instead of reading past the end.

A two-dimensional array

A 3 by 4 seating plan is printed row by row while free seats are counted per row and in total.

 C++
#include <iostream>

int main() {
    const int ROWS = 3;
    const int COLS = 4;
    char seats[ROWS][COLS] = {
        {'x', '.', '.', 'x'},
        {'.', '.', 'x', 'x'},
        {'.', '.', '.', '.'}
    };

    int freeSeats = 0;
    for (int r = 0; r < ROWS; ++r) {
        int freeInRow = 0;
        for (int c = 0; c < COLS; ++c) {
            std::cout << seats[r][c];
            if (seats[r][c] == '.') {
                ++freeInRow;
            }
        }
        std::cout << "  free: " << freeInRow << "\n";
        freeSeats += freeInRow;
    }
    std::cout << "Total free: " << freeSeats << "\n";
    return 0;
}

Output

x..x  free: 2
..xx  free: 2
....  free: 4
Total free: 8

The initialiser is one brace list per row, laid out like the plan itself. The outer loop picks a row, the inner loop walks along it; freeInRow is declared inside the outer loop so it restarts at zero for every row, while freeSeats sits outside both and accumulates. seats[r][c] is a single char, compared with the char literal '.'.

Plain array, std::array and std::vector

Featureint a[5]std::array<int, 5>std::vector<int>
size fixed at compile timeyesyesno, grows and shrinks
knows its own sizenoyes, .size()yes, .size()
copy with =noyesyes
bounds-checked accessno.at(i).at(i)
passed to a functiondecays to a pointer, size lostby value or reference, intactby value or reference, intact
headernone<array><vector>

Common mistakes

  • Using the size as an index

    Why it goes wrong: For int a[5] the valid indexes are 0 to 4. a[5] is one past the end; the compiler does not stop you, and the program reads or corrupts neighbouring memory.

    Fix: Loop with i < SIZE, never i <= SIZE, and refer to the last element as a[SIZE - 1].

     C++ · fix
    const int SIZE = 5;
    int a[SIZE] = {};
    for (int i = 0; i < SIZE; ++i) {
        a[i] = i * 10;
    }
  • Measuring an array inside a function with sizeof

    Why it goes wrong: An array parameter is really a pointer, so sizeof(a) / sizeof(a[0]) inside int count(int a[]) gives the size of a pointer divided by the size of an int (2 on this toolchain), not the element count. g++ warns about it.

    Fix: Pass the length as a second parameter, or take a std::array or std::vector by reference so the size travels with the data.

  • Sizing an array from input

    Why it goes wrong: int n; std::cin >> n; int a[n]; is a variable-length array, which is not part of standard C++; g++ accepts it only as an extension and warns under -pedantic. Other compilers reject it.

    Fix: Use std::vector<int> a(n); when the size is known only at run time.

  • Assuming a fresh array is full of zeros

    Why it goes wrong: int counts[26]; declared inside a function holds indeterminate values, so ++counts[i] increments garbage.

    Fix: Initialise with = {} to zero every element.

     C++ · fix
    int counts[26] = {};   // all zero

Where you use this

A fixed-size array is the natural frequency table. To count how often each lower-case letter appears in a line of text, declare int counts[26] = {}; and, for each letter c, increment counts[c - 'a']; the subtraction turns 'a' into index 0 and 'z' into index 25. The same shape counts votes for a fixed set of candidates, marks per grade band, or arrivals per hour of the day with a 24-element array. Because the size is part of the type and the indexes are computed directly from the data, the table needs no searching at all: one array access per item.

 C++ · in practice
int counts[26] = {};
for (char c : line) {
    if (c >= 'a' && c <= 'z') {
        ++counts[c - 'a'];
    }
}
std::cout << "e appears " << counts['e' - 'a'] << " times\n";

Key points

  • int a[5] holds five ints at indexes 0 to 4; the size must be a compile-time constant.
  • Initialise with a brace list; = {} zeroes every element, and no initialiser means garbage.
  • Indexes are never checked; reading or writing past the end is undefined behaviour.
  • std::array<T, N> (C++11, <array>) is a fixed-size array that knows its size, can be copied and offers .at().
  • Range-based for visits every element; use a reference loop variable to modify them.
  • A plain array passed to a function becomes a pointer and loses its size; pass the length or use std::array or std::vector.
  • Two-dimensional arrays are arrays of arrays, indexed grid[row][col] and walked with nested loops.

Try it yourself

Six sensor readings are stored in an array. Add a loop that counts how many of them are greater than 50 and store the answer in above, so the program prints Above 50: 3 for the given input.

Your program
#include <iostream>

int main() {
    const int N = 6;
    int readings[N];
    for (int i = 0; i < N; ++i) {
        std::cin >> readings[i];
    }
    int above = 0;
    // count the readings greater than 50
    std::cout << "Above 50: " << above << "\n";
    return 0;
}
Input the program receives: 12 63 50 71 8 99
Expected output: Above 50: 3

Practise this

Exercises for this lesson are in the C++ practice set.

Open the C++ playground

Frequently asked questions

How do I get the length of an array in C++?

A std::array or std::vector reports it with .size(). For a plain array you can write sizeof(a) / sizeof(a[0]), or std::size(a) from <iterator> in C++17, but only in the scope where the array was declared; inside a function that received the array as a parameter, both give the wrong answer because the parameter is a pointer. Passing the length alongside the array, or using std::array, avoids the problem.

Can a C++ array grow after it is declared?

No. The size of a plain array or a std::array is fixed when it is declared and is part of its type. When the number of elements changes while the program runs, use std::vector, which has push_back to append and resizes itself as needed.

What happens if I access an array out of bounds in C++?

The language does not define what happens. In practice the program reads or overwrites the memory next to the array, which may look fine, produce wrong numbers or crash, and the symptom can appear far from the faulty line. Using .at(i) on a std::array or std::vector turns the mistake into a std::out_of_range exception at the point of the bad access.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with GCC 13 (C++17) at build time by the publishing checks, and the output shown is what it printed. Running C++ inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C++17) locally.