Chapter 21: Fixed-Sized Arrays

Introduction

Fixed-sized arrays in C++ can be implemented using std::array and C-style arrays. This chapter covers both implementations and their usage.

std::array

std::array is a container that encapsulates fixed-size arrays. It provides better type safety and features compared to C-style arrays.

Basic Usage

#include <array>  // for std::array

std::array<int, 5> a;   // Members default initialized (int elements are left uninitialized)
std::array<int, 5> b{}; // Members value initialized (int elements are zero initialized) (preferred)

// Length must be constexpr, enum, or macro
constexpr std::array a1 { 9, 7, 5, 3, 1 }; // The type is deduced to std::array<int, 5>
constexpr auto myArray2 { std::to_array<int>({ 9, 7, 5, 3, 1 }) }; // Specify type only, deduce size

Accessing Elements

arr.size();
std::size(arr);
std::ssize(arr); // C++20

std::cout << std::get<3>(arr); // print the value of element with index 3
// std::cout << std::get<9>(arr); // invalid index (compile error)

Passing std::array to Functions

void passByRef(const std::array<int, 5>& arr);

template <typename T, std::size_t N>
void passByRef(const std::array<T, N>& arr);

template <std::size_t N>
void passByRef(const std::array<int, N>& arr); // We've defined the element type as int

Arrays of Compound Types

struct House
{
    int number{};
    int stories{};
    int roomsPerStory{};
};

std::array<House, 1> houses{};
houses[0] = { 13, 1, 7 };

constexpr std::array houses { House{ 13, 1, 7 } }; // CTAD

constexpr std::array<House, 3> houses { // initializer for houses
    { // extra set of braces to initialize the C-style array member with implementation-defined name
        { 13, 4, 30 }, // initializer for array element 0
     }
};

constexpr std::array<House, 3> houses
{
    ._Elems{
        { 13, 1, 7 }
    }
};

std::reference_wrapper

#include <functional> // for std::reference_wrapper

int x { 5 };
int y { 6 };
int z { 7 };

std::array<std::reference_wrapper<int>, 3> arr { x, y, z };
arr[1].get() = 5; // modify the object in array element 1

auto ref { std::ref(x) };   // C++11, deduces to std::reference_wrapper<int>
auto cref { std::cref(x) }; // C++11, deduces to std::reference_wrapper<const int>

C-Style Arrays

C-style arrays are the traditional array type in C++, but they have several limitations compared to std::array.

Basic Usage

int testScore[30] {};
int prime[5] { 2, 3, 5, 7, 11 };
int prime[] = { 2, 3, 5, 7, 11 }; // no length in []

sizeof(prime); // prints 20 (assuming 4 byte ints)

std::size(prime);    // C++17, returns unsigned integral value 5
std::ssize(prime);   // C++20, returns signed integral value 5

sizeof(array) / sizeof(array[0]);

template <typename T, std::size_t N>
constexpr std::size_t length(const T(&)[N]) noexcept
{ return N; }

Accessing Elements

void printElementZero(const int arr[]) // treated the same as const int*
{
    std::cout << arr[0];
}

Pointer Arithmetic

int x {};
const int* ptr{ &x }; // assume 4 byte ints

ptr;       // 00AFFD80
(ptr + 1); // 00AFFD84
(ptr - 1); // 00AFFD76

ptr = &arr[3];
*(ptr+1), ptr[1]; // arr[4]

C-Style Strings

C-style strings are arrays of characters ending with a null terminator (\0).

Basic Usage

const char str2[]{ "123" }; // const char[4], +1 for null terminator

const char name[] { "Alex" };        // const C-style string initialized with C-style string literal
const char* const color{ "Orange" }; // const pointer to C-style string literal

char rolls[255] {}; // declare array large enough to hold 254 characters + null terminator
std::cin.getline(rolls, std::size(rolls));

Functions for C-Style Strings

Multi-Dimensional Arrays

C-Style Multi-Dimensional Arrays

int a[3][5]{}; // a[row][col], C++ uses row-major order
int a[][][]{}; // there can be more dimensions
int array[3][5] // can omit (only) the leftmost length specification
{
    { 1, 2, 3, 4, 5 },     // row 0
    { 6, 7, 8, 9, 10 },    // row 1
    { 11, 12, 13, 14, 15 } // row 2
};

std::array Multi-Dimensional Arrays

std::array<std::array<int, 4>, 3> arr {{  // note double braces
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }}};

// std::array<std::array<int, col>, row> is backwards
using Array2dint34 = std::array<std::array<int, 4>, 3>;

// An alias template for a two-dimensional std::array
template <typename T, std::size_t Row, std::size_t Col>
using Array2d = std::array<std::array<T, Col>, Row>;

arr[0].size(), arr[0][0].size();

Conclusion

Fixed-sized arrays in C++ can be implemented using std::array and C-style arrays. std::array provides better type safety and features, while C-style arrays offer more flexibility at the cost of safety and ease of use. Understanding both types and their usage is crucial for writing efficient and maintainable C++ code.