Структуры и перечисления

Структуры


Структура struct – очень интересный тип данных: это набор переменных разного типа, объединённых одним именем. В некоторых случаях структуры позволяют сильно упростить написание кода и сделать его более понятным, а также упростить придумывание новых имён для переменных. А ещё структура – это практически класс (урок про классы)! Но без механизмов наследования и приватных-публичных членов.

Структура объявляется по следующей схеме:

struct ярлык {
  тип_данных имя_переменной_1;
  тип_данных имя_переменной_2;
  тип_данных имя_переменной_3;
};

Ярлык будет являться новым типом данных. Используя этот ярлык, можно объявлять уже непосредственно саму структуру как переменную:

ярлык имя_структуры;  // объявить одну структуру
ярлык имя_структуры_1, имя_структуры_2;  // объявить две структуры
ярлык имя_структуры[5];  // объявить массив структур

Также есть вариант объявления структуры без ярлыка, т.е. создаём структуру и сразу указываем её имя в конце.

struct {
  тип_данных имя_переменной_1;
  тип_данных имя_переменной_2;
  тип_данных имя_переменной_3;
} имя_структуры;
  • Обращение к члену структуры производится через точку: имя_структуры.имя_переменной и позволяет менять или читать значение.
  • Если две структуры объявлены одним ярлыком, то можно одну структуру просто приравнять к другой, все переменные запишутся соответственно на свои места.
  • Также можно изменить все значения переменных в структуре вот таким образом: имя_структуры = (ярлык) {значение_переменной_1, значение_переменной_2, значение_переменной_3};

Рассмотрим большой пример:

struct myStruct { // создаём ярлык myStruct
  boolean a;
  byte b;
  int c;
  long d;
  byte arr[5];
};

myStruct kek;  // создаём структуру kek

// создаём массив структур cheburek типа myStruct
myStruct cheburek[3];

void setup() {  
  // присвоим членам структуры значения вручную
  kek.a = true;
  kek.b = 10;
  kek.c = 1200;
  kek.d = 789456;
  kek.arr[0] = 10;
  kek.arr[1] = 20;
  kek.arr[2] = 30;

  // присвоим структуру kek структуре cheburek номер 0
  cheburek[0] = kek;

  // присвоим элемент массива из структуры kek 
  // структуре cheburek номер 1
  cheburek[0].arr[1] = kek.arr[1];

  // заполним данными структуру cheburek номер 2
  cheburek[2] = (myStruct) {
    false, 30, 3200, 321654, {1, 2, 3, 4, 5}
  };
}

Для чего нужны структуры? В большинстве примеров в интернете приводится использование структур для создания базы данных: имя, фамилия, номер телефона, адрес и т.д. Структуры очень удобно передавать по радио и другим интерфейсам связи, на моей практике – в структуре удобнее всего хранить настройки, потому что её можно целиком записать в EEPROM одной строчкой и одной строчкой оттуда же её прочитать, не заморачиваясь с адресами.

Вложенные структуры


Структуры также могут быть вложены друг в друга, доступ к нужному элементу осуществляется так же при помощи оператора “точка”, вот простой пример:

struct Values {
  int value1;
  float value2;
};

struct BigStruct {
  Values values;  // элемент типа Values
  int otherValue;
};

BigStruct myStruct;
myStruct.values.value2 = 3.14;

Перечисления


Перечисления (enum – enumeration) – тип данных, представляющий собой набор именованных констант, нужен для удобства написания программы. Допустим у нас есть переменная mode, отвечающая за номер режима работы устройства. Мы для себя запоминаем, какому значению переменной какой режим будет соответствовать, и где-нибудь себе записываем, например 0 – обычный режим, 1 – режим ожидания, 2 – режим настройки_1, 3 – режим настройки_2, 4 – калибровка, 5 – аварийный режим, ошибка. При написании или чтении программы часто придётся обращаться к этому списку, чтобы не запутаться. Можно сделать первый шаг по оптимизации: обозвать каждый режим при помощи константы (дефайна):

#define NORMAL 0
#define WAITING 1
#define SETTINGS_1 2
#define SETTINGS_2 3
#define CALIBRATION 4
#define ERROR_MODE 5

Таким образом вместо цифры можно будет использовать понятные слова и ориентироваться в коде будет гораздо проще. Но возникает проблема уникальности имён дефайнов.

Использование enum ещё немного упрощает эту задачу: перечисление позволяет создать переменную (по умолчанию имеет тип int), которая может принимать только те “названия”, которые для неё указаны. Это удобно также тем, что компилятор выдаст ошибку при создании перечислений с одинаковыми значениями. В случае с дефайнами программа просто будет работать неправильно! Объявление перечисления чем-то похоже на объявление структуры:

enum ярлык {имя1, имя2, имя3, имя4, имя5};

Таким образом мы объявили ярлык. Теперь, используя этот ярлык, можно объявить само перечисление:

ярлык имя_перечисления;

Также как и у структур, можно объявить перечисление без создания ярлыка (зачем нам лишняя строчка?):

enum {имя1, имя2, имя3, имя4, имя5} имя_перечисления;

Созданное таким образом перечисление является переменной, которая может принимать указанные для неё имена, также с этими именами её можно сравнивать. Теперь самое главное: имена для программы являются числами, начиная с 0 и далее по порядку увеличиваясь на 1. В абстрактном примере выше имя1 равно 0, имя2 равно 1, имя3 равно 2, и так далее. Помимо указанных имён, перечислению можно приравнивать числа напрямую, а также сравнивать с числами. Рассмотрим пример:

// создаём перечисление modes
// не создавая ярлык
enum {
  NORMAL,
  WAITING,
  SETTINGS_1,
  SETTINGS_2,
  CALIBRATION,
  ERROR_MODE,
} modes;

void setup() {
  Serial.begin(9600);   // для отладки
  modes = CALIBRATION;  // присваивание значения

  // можем сравнивать
  if (modes == CALIBRATION) {
    Serial.println("calibr");
  } else if (modes == ERROR_MODE) {
    Serial.println("error");
  }

  // присваиваем числом
  modes = 3;  // по нашему порядку это будет SETTINGS_2
}

Порядок автоматической нумерации можно изменить, задав начальное значение. От него всё будет и дальше изменяться на единицу:

enum {SET1 = 1, SET2, SET3, SET4, SET5} settings;

Таким образом SET1 имеет значение 1, SET2 будет 2 и так далее по порядку.

Полезные страницы


  • Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
  • Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
  • Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
  • Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
  • Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
  • Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
  • Поддержать автора за работу над уроками
  • Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту (alex@alexgyver.ru)
5/5 - (2 голоса)
Назад Оптимизация кода
Вперёд Объекты и классы
Подписаться
Уведомить о
guest
4 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии