Масиви

Часом буває необхідно визначити у програмі не одну змінну, а цілий ряд змінних одного типу. Якщо ми знаємо наперед, скільки таких змінних нам потрібно, і ця кількість не буде змінюватися, то у такому випадку зручно скористатися масивом.

Масивом називають структуру, яка складається з конкретної кількості однотипних змінних, які зберігаються разом в одній спільній послідовній області пам’яті. По суті, у пам’яті комп’ютера, вони розташовані один за одним у своєрідний “рядок” комірок, кожна з яких зберігає одне зі значень. Кожна така комірка має номер - індекс елемента у масиві. За цим індексом можна доступатися: читати чи записувати значення у комірці. Зазвичай індекси елементів нумеруються з нуля і до розмір масиву-1 відповідно.

Тип std::array

Для оголошення масиву, підключаємо тип std::array зі стандартної бібліотеки:

#include <array>

Тепер ми можемо оголосити масив розміру 3 (три елемента), які містить елементи типу int:

std::array<int, 3> integerArray;

Якщо нам необхідно, то ми можемо ініціалізувати масив набором значень:

std::array<char, 3> letters {'a', 'b', 'c'};

std::array підтримує схожий до std::vector набір операцій для доступу до елементів. Звичайно, операцій для додавання та вилучення елементів немає.

Приклад коду Пояснення
#include <array> Підключити визначення типу std::array зі стандартної бібліотеки.
std::array<int, 3> arr; Створюємо змінну arr- масив, який містить значення типу int та має розмір - 3 елемента. Замість int можна підставити інший необхідний нам тип.
std::array<int, 4> evenDigits {2, 4, 6, 8}; Створюємо масив та ініціалізовуємо його готовим набором значень.
arr.size() Повертає розмір масиву (кількість елементів). Кожен елемент має індекс (номер) - ціле число. Елементи індексуються по порядку починаючи з 0 до arr.size() - 1
arr.at(0) Повертає елемент з індексом 0. Елементи індексуються по порядку замість 0 до arr.size() - 1
arr[0] Повертає елемент з індексом 0. Елементи індексуються по порядку замість 0 до arr.size() - 1 Працює трохи швидше, оскільки немає перевірок стосовно виходу за межі масиву.
arr.fill(0) Заповнює усі комірки масиву заданим значенням (у даному випадку - обнулює всі комірки).

Чому не vector?

Дійсно, і масив і std::vector мають спільні риси:

  • Зберігають набір однотипних значень
  • Доступ до елемента відбувається за індексом

Але, напротивагу std::vector, масив має ряд відмінностей:

  • Займає трохи меньше пам’яті
  • Працює швидше ніж vector
  • Має фіксований розмір, який не можу змінюватися. Видаляти та додавати елементи неможна.

Існує ще кілька відмінностей, які можна буде зрозуміти коли ми дійдем до вивчення пам’яті та вказівників у С++. Але вони для нас наразі не суттєві. Головне, що оголошуючи масив, ми явно показуємо, що кількість елементів стала, та не може змінюватися. Таким чином, ми робимо нашу програму зрозумілішою, що врешті решт дозволяє легше у ній розібратися та уникнути помилок у майбутньому. Тому правилом на даному етапі вивчення для нас є: якщо кількість елементів не може змінюватися - оголошуємо масив.

Вимірність масиву

Оскільки, масив - це, грубо кажучи, рядок елементів, які зберігаються у пам’яті, то не зовсім очевидно, що з допомогою масивів можна змоделювати структури, які мають не один вимір (рядок) комірок, але й багатовімірні структури.

Наприклад, рядок з двох значень типу int нас не здивує:

std::array<int, 2> oneDimentionArray;

Якщо ми хочемо мати не тільки рядки, але й стовпці, то мусимо вкласти один масив у інший:

std::array<std::array<int, 2>, 2> twoDimentionsArray;

I навіть ми можемо змоделювати три виміри, оголосивши:

std::array<std::array<std::array<int, 2>, 2>, 2> threeDimentionsArray;

На малюнку видно, як виглядає рядок комірок, квадрат комірок та два шари комірок, які утворюють своєрідний “куб” у трьох вимірах. Також, показано, як всі ці три об’єкти можна трансформувати у ряд неподільних одновимірних масивів, один з яких містить значення, а інші - вкладені масиви.

array-dimentions

Приклад: Ігрове поле

Для того, щоб задати ігрове поле у програмі, ми можемо скористатися масивом. Оскільки поле має ширину та висоту, створимо двовимірний масив. Для кількості рядків та колонок клітинок, створимо окремі константи, щоб нам зручно було використовувати у програмі та змінювати ці значення:

const int mazeColumns = 20;
const int mazeRows = 20;

std::array<std::array<char, mazeColumns>, mazeRows> maze;

Тепер, для прикладу, можемо ініціалізувати масив стінками по периметру. Для цього ідемо по рядках та стовпцях, перевіряємо, чи це не перша клітинка рядка чи стовпця. Якщо так - то ставимо стінку, інакше - залишаємо пустий простір.

const char emptySymbol = ' ';
const char wallSymbol = 'X';

for (int row = 0; row < mazeRows; row++)
{
    for (int column = 0; column < mazeColumns; column++)
    {
        if ((row == 0) || (row == mazeRows - 1) || (column == 0) || (column == mazeColumns - 1))
        {
            maze[row][column] = wallSymbol;
        }
        else
        {
            maze[row][column] = emptySymbol;
        }
    }
}

Також, пройшовшись по рядкам та стовпцям, ми можемо вивести ігрове поле на екран. Для цього потрібно виводити по одному символу, а переводити рядок лише після того, як цілий рядок ігрового поля виведено на екран. Врешті, винесемо ініціалізацію поля та вивід його на екран у окремі функції, щоб логічно відділити кожну з цих підпрограм та дати їм назви. У результаті отримаємо таку програму:

#include <iostream>
#include <array>

const char emptySymbol = ' ';
const char wallSymbol = 'X';

const int mazeColumns = 20;
const int mazeRows = 20;

void initializeMaze(std::array<std::array<char, mazeColumns>, mazeRows> &rMaze)
{
    for (int row = 0; row < mazeRows; row++)
    {
        for (int column = 0; column < mazeColumns; column++)
        {
            if ((row == 0) || (row == mazeRows - 1) || (column == 0) || (column == mazeColumns - 1))
            {
                maze[row][column] = wallSymbol;
            }
            else
            {
                maze[row][column] = emptySymbol;
            }
        }
    }
}

void drawMaze(const std::array<std::array<char, mazeColumns>, mazeRows> &rMaze)
{
    for (int row = 0; row < cMazeRows; row++)
    {
        for (int column = 0; column < cMazeColumns; column++)
        {
                char ch = maze[row][column];
                std::cout << ch;
        }
        std::cout << std::endl;
    }
}

int main()
{
    std::array<std::array<char, mazeColumns>, mazeRows> maze;
    initializeMaze(maze);
    drawMaze(maze);
}