Лекция №8. Массивы
Массив (array) - это совокупность переменных одного типа, к которым можно обратиться с помощью общего имени и индекса, т.е. номера элемента в массиве. По сути это набор переменных, которые называются одним именем и имеют личные номера. Для объявления массива достаточно указать квадратные скобки после имени переменной с любым типом данных.
Указать компилятору размер массива можно двумя способами: явным числом в квадратных скобках, либо при объявлении записать в каждую ячейку значение, тогда компилятор сам посчитает их количество. Рассмотрим пример объявления массива разными способами:
// указываем количество ячеек
byte myInts[6];
// указываем содержимое ячеек, компилятор сам посчитает их количество
int myPins[] = {2, 4, 8, 3, 6,};
// указываем и то и то, количество ячеек в [ ] должно совпадать с { } или быть больше!
float Sens[3] = {0.2, 0.4, -8.5};
// храним символы
char message[6] = {'h', 'e', 'l', 'l', 'o'};
Размер массива, объявленного глобально, должен быть задан однозначно: конкретным числом, константой const или константой #define, так как массив будет существовать на всём протяжении работы программы и не может менять свой размер.
Размер массива, объявленного локально, может быть задан переменной, так как такой массив создаётся во время выполнения программы и удаляется из памяти после закрывающей фигурной скобки.
После указания последнего элемента массива можно ставить запятую, компилятор её проигнорирует (см. пример выше, массив myPins).
Массив символов является строкой, о них мы поговорим в отдельном уроке.
// размеры
#define arr1_size 10
const byte arr2_size = 20;
byte arr3_size = 30;
// массивы
int arr0[5];
int arr1[arr1_size];
int arr2[arr2_size];
//int arr3[arr3_size]; // приведёт к ошибке!
Обращение к элементам
Обращение к элементу массива осуществляется точно так же, в квадратных скобках. Важно помнить, что отсчёт в программировании начинается с нуля, и первый элемент массива имеет номер 0 (ноль):
// первый элемент массива myInts равен 50
myInts[0] = 50;
// записать в ячейку 3 массива myPins
// значение элемента 0 массива myInts
myPins[3] = myInts[0];
Размер массива
Для определения размера массива можно воспользоваться функцией sizeof(), которая вернёт размер массива в количестве байтов. Зачем? Допустим в программе массив задаётся без указания размера, но элементы задаются явно (в фигурных скобках) и в процессе разработки программы размер массива может меняться. Чтобы не пересчитывать размер вручную и не менять его везде в программе, можно использовать эту функцию.
Примечание: если размер массива неизвестен на момент компиляции программы - sizeof() не сможет его посчитать и выдаст размер указателя (2 байта на AVR).
Ещё один момент: sizeof(имя_массива) даёт вес массива, а не количество элементов в нём! Если массив состоит из элементов типа byte - его вес совпадёт с размером. В остальных случаях нужно разделить вес массива на вес одного элемента, например так: sizeof(arr) / sizeof(arr[0]) - делим на вес элемента, чтобы не привязываться к типам данных. Результат вычисляется на этапе компиляции и в процессе работы программы время на вычисление не тратится.
Многомерные массивы
Выше мы рассмотрели одномерные массивы, в которых элементы определяются просто порядковым номером (индексом). Можно задавать и многомерные массивы, в которых адрес элемента будет определяться несколькими индексами. Например двумерный массив, он же матрица, он же таблица, каждый элемент имеет номер строки и столбца. Задаётся такой массив следующим образом: тип имя[строки][столбцы]
// двумерный массив, 5 строк 10 столбцов
byte myArray[5][10];
// матрица 3х4
byte myMatrix[][4] = {
{10, 11, 12, 13},
{14, 15, 16, 17},
{18, 19, 20, 21},
};
// матрица 2х3
byte myTable[][3] = {{10, 11, 12}, {13, 14, 15}};
Очень важно помнить, что при объявлении массива с вручную вписанными данными нужно обязательно указать размер количества ячеек в измерении на 1 меньше размерности массива (для двумерного - обязательно указать размер одного из измерений, для трёхмерного - два, и т.д.).
В рассмотренном выше двумерном массиве myMatrix элемент с адресом 0, 2 (строка 0 столбец 2) имеет значение 12. Обращение к этому элементу например с целью перезаписи будет выглядеть так:
// меняем 12 на 20, ячейка 0,2
myMatrix[0][2] = 20;
С элементами массивов можно производить такие же действия, как с обычными переменными, т.е. всю математику, которую мы рассматривали в предыдущем уроке. Также не стоит забывать, что массивом может быть почти любой тип данных: численные переменные, структуры, классы, строки... Область видимости точно так же применяется к массивам, ведь массив - это по сути обычная переменная.
Функции для работы с массивами
В C++ есть несколько полезных функций для работы с массивами:
memset(buf, val, len) - заполнить байтовый массив buf длиной len значением val
memcmp(buf1, buf2, len) - сравнить массивы buf1 и buf2 на длину len. Вернёт 0 если массивы одинаковые
memcpy(dest, src, len) - скопировать массив src в массив dest на длину len
Пример:
byte src[5] = {1, 2, 3, 4, 5};
// src == {1, 2, 3, 4, 5}
byte dst[5]; // "пустой" массив
memset(dst, 123, 5); // заполнить dst значением 123
// dst == {123, 123, 123, 123, 123}
// сравнить
Serial.println(memcmp(src, dst, 5) == 0); // false
// скопировать из src в dst
memcpy(dst, src, 5);
// dst == {1, 2, 3, 4, 5}
// сравнить
Serial.println(memcmp(src, dst, 5) == 0); // true