Introduction

There are two types of arrays in C++. The first of which is derived from C, and the other was introducted in the C++11 specification and is known as std::array, and is part of the containers library. Both of these arrays have their sizes evaluated at compile time, so if you are after a data structure like an array but with a size that can grow or shrink at run time, consider using std::vector. This guide will cover both C-style arrays and std::array. Before that, I will first discuss how an array is stored in memory:

How an array is stored in memory.

An array is a collection of values of the same type. These values are stored sequentially in memory. Thus, an array of 4 byte signed integers of length 3 takes up 12 bytes of memory. See the image below: Value of array element 3: 556272128

Using C-style arrays

C-style arrays in C++ can be created as follows:

int arr[3];

Where int is the type of array, arr is the name/identifier of the array and 3 is the size of the array. You can also initialize some or all of the array elements like this:

int arr[3] {2, 4, 6};

It is important to note that this will create an array in a memory location known as the stack, which has a limited data capacity, but allows for fast data access. For simple programs, this size limitation is not an issue, but for programs which frequently make use of the stack, stack overflow errors can occur. Consider the following example:

int arr[300000000];

This will cause a segmentation fault as the amount of memory required to store this array exceeds the size of the stack. However, it is still possible to create an array of this size by making use of the heap. This can be done as follows:

int* arr = new int[300000000];
delete[] arr;

The above example deletes the array immediately after use, which is done to avoid a memory leak.

Array elements can be later accessed via the [] operator, shown by the following example:

std::cout << arr[0];

This will print out the first element of the array, which is 2. The number inside the square brackets is known as the index, which represents which element to get out of the array. Notice how the first element is retrieved via an index of 0. This is because arrays are zero-indexed, meaning that to get the first element of the array, an index value of 0 must be used, and to get the last element of the array, a value equal to the size of the array minus 1 must be used as the index. For example, to get the last element of an array of 3 elements, an index of 2 must be used. It is easy to exceed the length of the array, however it is easier to deal with these problems by making use of C++’s implementation of an array, known as std::array.

Using std::array

In order to use C++-style arrays, the array library must be included into the project via adding #include <array>. std::array takes a typename, which can be anything, and an integer which represents the length of the array to create. To demonstrate this, here is an example:

std::array<int, 3> arr {3, 6, 9};

The type we are providing is an integer type, and we are specifying the length to be 3 array elements. We can also initialize the values of the array to be 3, 6 and 9.

To get access to an element of the array, C-style syntax can be used:

arr[0];

This will evaluate to the first element of array arr, which is the value 3. It is important to remember that elements of both C-style arrays and std::arrays are zero-indexed, meaning that to get the first element of the array, the index must be 0. A common mistake is to assume that the last element of the array can be indexed by the size of the array. This leads to memory being accessed that is outside of the array, the value of which can be anything. To avoid mistakes like this, elements witht an std::array can also be accessed via the at() method:

arr.at(3);

When the array is indexed out of bounds via the at() method, the program terminates with a helpful error message as to why: terminate called after throwing instance of 'std::out_of_range'

These off-by-one errors are common when looping over an array. Consider the following incorrect example:

int arr[3] {2, 4, 6};
for (int i = 0; i <= 3; i++) {
    std::cout << "Value of array element " << i << ": " << arr[i] << "\n";
}
std::cout.flush();

This will print out:

Value of array element 0: 2 Value of array element 1: 4 Value of array element 2: 6 Value of array element 3: 556272128

The loop is running 4 times instead of 3, and is trying to access an index outside the range of the array. This means that memory is returned from outside of the array, the data of which could be anything. One of the ways in which C++ programmers can avoid this is to use iterators.

A closer look at arrays and the [] operator

Previous examples have used the [] operator to get access specific elements of the array, which were subsequently printed out. But what if we don’t use the [] operator?

std::cout << "arr: " << arr << std::endl;

Printing the identifier arr directly gives the result:

arr: 0x7ffd5fc3c8fc

Which is the address of the first element of the array, represented in hexadecimal. The array arr is simply a pointer to the start of the array, and we can manipulate this pointer to get other elements of the array. If we add one the address of arr and dereference it, we can get the second element of the array, which is 4. Thus:

std::cout << "arr[1]" << *(arr+1) << std::endl;

Gives the result:

arr: 4

Note the use of brackets: the address must be incremented by one before dereferencing. Without brackets, the dereference operator would have been evaluated first, which would ultimately result in the printed value the incorrect value of 3. Using the [] operator is a convenient way of referring to elemnts of the array as it avoids problems like these. The same result can be achieved via arr[1].

Avoiding off-by-one errors by making use of iterators

In order to use iterators, the iterator library must be included into the project via adding #include <iterator>. Iterators are just an abstraction of pointers

#include <array>
#include <iostream>
#include <iterator>

int main(int argc, char** argv) {
    std::array<int, 5> a {5};
    for (std::array<int, 5>::iterator it = a.begin(); it < a.end(); it++) {
        std::cout << *it << '\n';
    }
    std::cout.flush();
}