Массивы


Массив представляет набор однотипных данных. Формальное определение массива выглядит следующим образом:

тип_переменной название_массива [длина_массива]
После типа переменной идет название массива, а затем в квадратных скобках его размер. Например, определим массив из 4 чисел:

int numbers[4];
Данный массив имеет четыре числа, но все эти числа имеют неопределенное значение. Однако мы можем выполнить инициализацию и присвоить этим числам некоторые начальные значения через фигурные скобки:

int numbers[4] = {1,2,3,4};
Значения в фигурных скобках еще называют инициализаторами. Если инициализаторов меньше, чем элементов в массиве, то инициализаторы используются для первых элементов. Если в инициализаторов больше, чем элементов в массиве, то при компиляции возникнет ошибка:

int numbers[4] = {1, 2, 3, 4, 5, 6};
Здесь массив имеет размер 4, однако ему передается 6 значений.

Если размер массива не указан явно, то он выводится из количества инициализаторов:

int numbers[] = {1, 2, 3, 4, 5, 6};
В данном случае в массиве есть 6 элементов.

Свои особенности имеет инициализация символьных массивов. Мы можем передать символьному массиву как набор инициализаторов, так и строку:

char s1[] = {'h', 'e', 'l', 'l', 'o'};
char s2[] = "world";
Причем во втором случае массив s2 будет иметь не 5 элементов, а 6, поскольку при инициализации строкой в символьный массив автоматически добавляется нулевой символ '\0'.

При этом не допускается присвоение одному массиву другого массива:

int nums1[] = {1,2,3,4,5};
int nums2[] = nums1; // ошибка
nums2 = nums1; // ошибка
После определения массива мы можем обратиться к его отдельным элементам по индексу. Индексы начинаются с нуля, поэтому для обращения к первому элементу необходимо использовать индекс 0. Обратившись к элементу по индексу, мы можем получить его значение, либо изменить его:

#include <iostream>

int main()
{
int numbers[4] = {1,2,3,4};
int first_number = numbers[0];
std::cout << first_number << std::endl; // 1
numbers[0] = 34; // изменяем элемент
std::cout << numbers[0] << std::endl; // 34

return 0;
}
Число элементов массива также можно определять через константу:

const int n = 4;
int numbers[n] = {1,2,3,4};
Перебор массивов
Используя циклы, можно пробежаться по всему массиву и через индексы обратиться к его элементам:

#include <iostream>

int main()
{
int numbers[4] = {1,2,3,4};
int size = sizeof(numbers)/sizeof(numbers[0]);
for(int i=0; i < size; i++)
std::cout << numbers[i] << std::endl;

return 0;
}
Чтобы пройтись по массиву в цикле, вначале надо найти длину массива. Для нахождения длины применяется оператор sizeof. По сути длина массива равна совокупной длине его элементов. Все элементы представляют один и тот же тип и занимают один и тот же размер в памяти. Поэтому с помощью выражения sizeof(numbers) находим длину всего массива в байтах, а с помощью выражения sizeof(numbers[0]) - длину одного элемента в байтах. Разделив два значения, можно получить количество элементов в массиве. А далее с помощью цикла for перебираем все элементы, пока счетчик i не станет равным длине массива. В итоге на консоль будут выведены все элементы массива:

Но также есть и еще одна форма цикла for, которая предназначена специально для работа с коллекциями, в том числе с массивами. Эта форма имеет следующее формальное определение:

for(тип переменная : коллекция)
{
инструкции;
}
Используем эту форму для перебора массива:

#include <iostream>

int main()
{
int numbers[4] = {1,2,3,4};
for(int number : numbers)
std::cout << number << std::endl;

return 0;
}
При переборе массива каждый перебираемый элемент будет помещаться в переменную number, значение которой в цикле выводится на консоль.

Если нам неизвестен тип объектов в массиве, то мы можем использовать спецификатор auto для определения типа:

for(auto number : numbers)
std::cout << number << std::endl;
Многомерные массивы
Кроме одномерных массивов в C++ есть многомерные. Элементы таких массивов сами в свою очередь являются массивами, в которых также элементы могут быть массивами. Например, определим двухмерный массив чисел:

int numbers[3][2];
Такой массив состоит из трех элементов, при этом каждый элемент представляет массив из двух элементов. Инициализируем подобный массив:

int numbers[3][2] = { {1, 2}, {4, 5}, {7, 8} };
Вложенные фигурные скобки очерчивают элементы для каждого подмассива. Такой массив еще можно представить в виде таблицы:

1 2
4 5
7 8
Также при инициализации можно опускать фигурные скобки:

int numbers[3][2] = { 1, 2, 4, 5, 7, 8 };
Возможна также инициализация не всех элементов, а только некоторых:

1
int numbers[3][2] = { {1, 2}, {}, {7} };
И чтобы обратиться к элементам вложенного массива, потребуется два индекса:

int numbers[3][2] = { {1, 2}, {3, 4}, {5, 6} };
std::cout << numbers[1][0] << std::endl; // 3
numbers[1][0] = 12; // изменение элемента
std::cout << numbers[1][0] << std::endl; // 12
Переберем двухмерный массив:

#include <iostream>

int main()
{
const int rows = 3, columns = 2;
int numbers[rows][columns] = { {1, 2}, {3, 4}, {5, 6} };
for(int i=0; i < rows; i++)
{
for(int j=0; j < columns; j++)
{
std::cout << numbers[i] [j] << "\t";
}
std::cout << std::endl;
}
return 0;
}
Также для перебора элементов многомерного массива можно использовать другую форму цикла for:

#include <iostream>

int main()
{
const int rows = 3, columns = 2;
int numbers[rows][columns] = { {1, 2}, {3, 4}, {5, 6} };

for(auto &subnumbers : numbers)
{
for(int number : subnumbers)
{
std::cout << number << "\t";
}
std::cout << std::endl;
}

return 0;
}
Для перебора массивов, которые входят в массив, применяются ссылки. То есть во внешнем цикле for(auto &subnumbers : numbers) &subnumbers представляет ссылку на подмассив в массиве. Во внутреннем цикле for(int number : subnumbers) из каждого подмассива в subnumbers получаем отдельные его элементы в переменную number и выводим ее значение на консоль.