array

Массив объединяет несколько переменных одного типа под одним именем — обращайтесь к элементам по индексу и удобно обрабатывайте их в цикле for.

Полная статья

Описание

Массив объединяет несколько переменных одного типа под одним именем. Вместо того чтобы заводить отдельные переменные sensor1, sensor2, sensor3 и так далее, вы объявляете один массив и обращаетесь к его элементам по индексу. Это особенно удобно, когда нужно обрабатывать серию однотипных данных: показания датчиков, яркость светодиодов, ноты мелодии.

В Arduino используется C++, поэтому массивы здесь работают по правилам этого языка — строго типизированы, фиксированного размера и без автоматической проверки границ.

Создание (объявление) массива

Существует несколько равнозначных способов объявить массив:

      // Объявление массива заданной длины без инициализации значений:
      int myInts[6];

      // Объявление массива без явного указания размера (компилятор
      // подсчитывает элементы и создаёт массив подходящего размера):
      int myPins[] = {2, 4, 8, 3, 6, 4};

      // Объявление массива заданной длины с инициализацией значений:
      int mySensVals[5] = {2, 4, -8, 3, 2};

      // При объявлении массива типа char его нужно сделать на один элемент длиннее,
      // чтобы вместить обязательный нулевой символ завершения строки:
      char message[6] = "hello";

Можно задать только размер — тогда элементы будут содержать случайные значения из памяти, пока вы их не инициализируете. Можно сразу перечислить значения — компилятор сам посчитает размер. Можно указать и размер, и начальные значения одновременно.

Доступ к элементам массива

Индексация в C++ начинается с нуля. Это значит, что первый элемент массива имеет индекс 0, второй — 1, и так далее:

mySensVals[0] == 2, mySensVals[1] == 4, и так далее.

Если массив объявлен на десять элементов, последний из них имеет индекс 9, а не 10:

    int myArray[10]={9, 3, 2, 4, 3, 2, 7, 8, 9, 11};
    // myArray[9]    содержит 11
    // myArray[10]   недопустимый индекс, содержит случайные данные (другой адрес памяти)

Это одна из самых распространённых ошибок в работе с массивами. Обращение по индексу, равному размеру массива или большему, — это выход за пределы выделенной памяти. Компилятор C++ такую ошибку не поймает: программа скомпилируется, но при запуске начнёт читать или перезаписывать чужую память. Последствия непредсказуемы: неверные данные, зависание скетча, перезапуск платы. Отлаживать такие ошибки особенно неприятно, потому что симптомы могут проявляться совсем не там, где находится ошибка.

В отличие от Java или Python, C++ не генерирует исключение при выходе за границы массива — он просто обращается к произвольной ячейке памяти.

Запись значения в массив:

mySensVals[0] = 10;

Чтение значения из массива:

x = mySensVals[4];

Массивы и циклы for

Массивы особенно удобны в паре с циклом for: счётчик цикла выступает индексом, и вы можете последовательно обработать все элементы без дублирования кода. Например, чтобы вывести весь массив в Serial Monitor:

    for (byte i = 0; i < 5; i = i + 1) {
      Serial.println(myPins[i]);
    }

Тот же подход используется для заполнения массива, поиска минимума и максимума, усреднения показаний датчиков и любой другой обработки серии значений.

Где применяется

  • Хранение показаний датчиков — записать несколько измерений подряд и затем усреднить их, чтобы сгладить шум.
  • Управление группой светодиодов — хранить номера пинов или уровни яркости в массиве и в цикле применять их к каждому светодиоду.
  • Воспроизведение мелодии — держать частоты нот и их длительности в двух массивах и перебирать их в цикле.
  • Буфер для приёма данных — накапливать байты из Serial в массив символов перед разбором команды.

Практические замечания

  • Размер массива задаётся на этапе компиляции и не может измениться во время выполнения программы. Если нужна динамическая коллекция, это уже другая тема (динамическое выделение памяти), и для Arduino-новичков она не рекомендуется.
  • Всегда проверяйте, что индекс в цикле не превышает размер - 1. Безопаснее всего использовать конструкцию for (int i = 0; i < размер; i++).
  • Если вы объявляете массив без инициализации, его элементы содержат «мусор» — случайные значения, оставшиеся в памяти. Инициализируйте явно или заполняйте в setup().
  • Глобальные массивы занимают статическую RAM всё время работы программы. На платах с малым объёмом памяти (например, Arduino Uno с 2 КБ RAM) большие массивы могут вытеснить стек и вызвать трудноуловимые сбои.
  • Чтобы узнать количество элементов массива в коде, используйте выражение sizeof(arr) / sizeof(arr[0]) — это безопаснее, чем хранить размер в отдельной переменной и рисковать рассинхронизацией.

Пример кода

Полную программу с демонстрацией массивов можно посмотреть во встроенном примере Arduino: Arrays.

Полезные материалы