Являются ли глобальные переменные злыми в Arduino?

Я относительно новичок в программировании, и многие из лучших
методов кодирования, которые я читаю, эффективно утверждают, что
существует очень мало веских причин использовать глобальную
переменную (или что лучший код вообще не имеет глобальных
переменных).

Я сделал все возможное, чтобы помнить об этом при написании
программного обеспечения для создания интерфейса Arduino с
SD-картой, поговорить с компьютером и запустить контроллер
двигателя.

В настоящее время у меня есть 46 глобалов для примерно 1100
строк кода «начального уровня» (нет строки, содержащей более одного
действия). Является ли это хорошим соотношением или я должен
смотреть на его уменьшение? Также, какие практики я могу
использовать для сокращения количества глобальных глобалов?

Я спрашиваю об этом здесь, потому что я специально занимаюсь
передовыми методами кодирования продуктов Arduino, а не
компьютерным программированием в целом.

Они не *зло* как таковые, но они, как правило,
злоупотребляют, когда нет веских оснований для их
использования.

Существует много раз, когда глобальные переменные выгодны. Тем
более, что программирование Arduino под капотом значительно
отличается от программирования ПК.

Самое большое преимущество глобальных переменных - это
статическое распределение. Особенно с большими и сложными
переменными, такими как экземпляры классов. Динамическое
распределение (использование `new` и т. Д.)
Неодобрительно из-за нехватки ресурсов.

Кроме того, вы не получаете ни одного дерева вызовов, как в
обычной программе C (одна функция `main()` , вызывающая
другие функции) - вместо этого вы фактически получаете два
отдельных дерева ( `setup()` , а затем функции
`loop()` ), что означает, что иногда глобальные
переменные являются единственным способом достижения вашей цели (т.
е. если вы хотите использовать ее как в `setup()` и
`loop()` ).

Так что нет, они не злые, и на Arduino у них все больше и больше
пользы, чем на ПК.



Очень сложно дать окончательный ответ, не видя своего
фактический код.

Глобальные переменные не являются злыми, и они часто имеют смысл
во встроенных где вы обычно делаете большой доступ к аппаратным
средствам. У тебя есть только четыре UARTS, только один порт I2C и
т. д. Поэтому имеет смысл использовать глобальные переменные для
переменных, привязанных к конкретным аппаратным ресурсам. И
действительно, основная библиотека Arduino делает это:
`Serial` , `Serial1` и т. д. являются
глобальными переменные. Кроме того, переменная, представляющая
глобальное состояние программы обычно является глобальным.

> 
> В настоящее время у меня есть 46 глобалов для примерно 1100
> строк [code]. Это   хорошее соотношение [...]
> 
> 
> 

Не о цифрах. Правильный вопрос, который вы должны задать себе,
для каждого из этих глобалов, имеет ли смысл иметь его в глобальном
объем.

Тем не менее, 46 глобальных кажется мне немного выше. Если
некоторые из них сохраняются постоянными значения, квалифицируйте
их как `const` : компилятор обычно оптимизирует их
хранения. Если какая-либо из этих переменных используется только в
одном функцию, сделать ее локальной. Если вы хотите, чтобы его
значение сохранялось между вызовами к функции, квалифицируйте ее
как `static` . Вы также можете уменьшить количество
«видимых» глобалов путем группирования переменных вместе внутри
класс и имеющий один глобальный экземпляр этого класса. Но делайте
это только тогда, когда имеет смысл собирать вещи вместе. Создание
большого класса `GlobalStuff` ради того, чтобы иметь
только одну глобальную переменную, код clearer.



Основная проблема с глобальными переменными - обслуживание кода.
При чтении строки кода легко найти объявление переменных,
переданных как параметр или объявленных локально. Нелегко найти
объявление глобальных переменных (часто это требует и IDE).

Когда у вас много глобальных переменных (40 уже много),
становится трудно иметь явное имя, которое не слишком велико.
Использование пространства имен - это способ выяснить роль
глобальных переменных.

Плохой способ имитации пространств имен в C:

```
static struct {
    int motor1, motor2;
    bool sensor;
} arm;

```

На процессоре intel или arm доступ к глобальным переменным
медленнее, чем другие переменные. Наверное, на ардуине.



Хотя я не буду использовать их при программировании для ПК, для
Arduino у них есть некоторые преимущества. Большинство, если уже
сказано:

* Использование динамической памяти (создание пробелов в
ограниченном пространстве кучи Arduino)
* Доступно везде, поэтому нет необходимости передавать их в
качестве аргументов (что требует пространства стека)

Кроме того, в некоторых случаях, особенно с точки зрения
производительности, может быть полезно использовать глобальные
переменные, а не создавать элементы при необходимости:

* Чтобы уменьшить пробелы в области кучи
* Динамическое выделение и/или освобождение памяти может занять
значительное время
* Переменные могут быть «повторно использованы», как список
элементов глобального списка, которые используются по нескольким
причинам, например. один раз в качестве буфера для SD, а затем в
качестве временного буфера строк.



Как и во всем (кроме мифов, которые действительно злы), их место
занимают глобалы.

например Если у вас есть отладочный последовательный порт или
файл журнала, который вам нужен, чтобы писать везде, то часто имеет
смысл сделать его глобальным. Аналогично, если у вас есть
критическая информация о статусе системы, тогда ее глобальное
решение часто является самым простым решением. Нет смысла иметь
значение, которое вы должны передать каждой отдельной функции в
программе.

Как говорили другие 46, кажется, что всего лишь для более 1000
строк кода, но, не зная деталей того, что вы делаете, трудно
сказать, если вы закончите использовать их или нет.

Однако для каждого глобального вопроса задайте себе несколько
важных вопросов:

Является ли это имя понятным и конкретным, чтобы я случайно не
использовал одно и то же имя в другом месте? Если не изменить
имя.

Нужно ли это когда-либо менять? Если нет, то подумайте о том,
чтобы сделать его const.

Должно ли это быть видимым повсюду или оно глобально, чтобы
поддерживать значение между вызовами функций? Разве это следует
считать локальным для функции и использовать ключевое слово
static.

Будут ли действительно сильно испортиться, если это изменится
частью кода, когда я не буду осторожен? например если у вас есть
две связанные переменные, например, имя и идентификационный номер,
которые являются глобальными (см. предыдущее примечание об
использовании глобального, когда вам нужна информация почти везде),
тогда, если один из них будет изменен без других неприятных вещей,
это может случиться. Да, вы могли бы просто быть осторожными, но
иногда хорошо прикладывать осторожность. например, поместите их в
другой файл .c, а затем определите функции, которые устанавливают
оба из них одновременно, и позволяют читать их. Затем вы включаете
только функции в связанный файл заголовка, таким образом, остальная
часть вашего кода может обращаться к переменным только через
определенные функции и поэтому не может испортить вещи. Это, в
основном, чистый способ сделать то, что позволял бы класс c ++, но
без необходимости учиться определять и создавать классы, чтобы вы
не слишком много учились.

- update - Я только понял, что вы спросили о лучшей практике
Arduino, а не об общем кодировании, и это скорее общий ответ на
кодирование. Но, честно говоря, нет большой разницы, хорошая
практика - хорошая практика. Структура `startup()` и
`loop()` Arduino означает, что вам нужно использовать
глобальные переменные немного больше, чем другие платформы в
некоторых ситуациях, но это не сильно меняется, вы всегда в
конечном итоге стремясь к лучшему, что вы можете сделать в рамках
ограничений платформы, независимо от платформы.



Они злы? Может быть. Проблема с глобальными переменными
заключается в том, что они могут быть доступны и изменены в любой
момент времени с помощью любой функции или исполняемого кода без
ограничений. Это может привести к ситуациям, которые, скажем,
трудно отследить и объяснить. Поэтому желательно минимизировать
количество глобалов, если возможно, вернуть сумму к нулю.

Их можно избежать? Почти всегда да. Проблема с Arduino
заключается в том, что они заставляют вас использовать этот два
функциональных подхода, в которых они предполагают, что вы
`setup()` , и вы `loop()` . В этом конкретном
случае у вас нет доступа к объему функции вызывающего абонента из
этих двух функций (возможно, `main()` )). Если бы у вас
было, вы могли бы избавиться от всех глобалов и вместо этого
использовать местных жителей.

Представьте следующее:

```
int main() {
  setup();

  while (true) {
    loop();
  }
  return 0;
}

```

Это, вероятно, более или менее то, что выглядит основная функция
программы Arduino. Вместо этого переменные, необходимые как для
`setup()` , так и для функции `loop()` ,
будут предпочтительно объявлены внутри области `main()`
а не глобального масштаба. Затем они могли быть доступны для двух
других функций посредством передачи их в качестве аргументов (при
необходимости, с помощью указателей).

Например:

```
int main() {
  int myVariable = 0;
  setup(&myVariable);

  while (true) {
    loop(&myVariable);
  }
  return 0;
}

```

Обратите внимание, что в этом случае вам также необходимо
изменить подпись обеих функций.

Поскольку это может оказаться невозможным и нежелательным, я
вижу действительно единственный способ удалить большинство
глобальных программ из программы Arduino без изменения структуры
принудительной структуры.

Если я правильно помню, вы отлично можете использовать C ++ при
программировании для Arduino, а не C. Если вы еще не знакомы с
[инкапсуляция данных](https://en.wikipedia.org/wiki/Object%20-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%20%C2%ABrel%20=%C2%AB%20nofollow%20noreferrer%20%C2%BB%3E%20%D0%9E%D0%9E%D0%9F%20(%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)%20%3C/a%3E%20%D0%B8%D0%BB%D0%B8%20C%20++,%20%D0%BC%D0%BE%D0%B6%D0%B5%D1%82%20%D0%BF%D0%BE%D1%82%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%D1%81%D1%8F%20%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%BE%D0%B5%20%D0%BF%D1%80%D0%B8%D0%B2%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%B8%20%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%BE%D0%B5%20%D1%87%D1%82%D0%B5%D0%BD%D0%B8%D0%B5.%3C/p%3E%3Cp%3E%D0%9C%D0%BE%D0%B5%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%20%D0%B1%D1%8B%D0%BB%D0%BE%20%D0%B1%D1%8B%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D0%BA%D0%BB%D0%B0%D1%81%D1%81%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B%20%D0%B8%20%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C%20%D0%B5%D0%B4%D0%B8%D0%BD%D1%8B%D0%B9%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9%20%D1%8D%D0%BA%D0%B7%D0%B5%D0%BC%D0%BF%D0%BB%D1%8F%D1%80%20%D1%8D%D1%82%D0%BE%D0%B3%D0%BE%20%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%B0.%20%D0%9A%D0%BB%D0%B0%D1%81%D1%81%20%D0%B4%D0%BE%D0%BB%D0%B6%D0%B5%D0%BD%20%D1%81%D1%87%D0%B8%D1%82%D0%B0%D1%82%D1%8C%D1%81%D1%8F%20%D0%BF%D0%BB%D0%B0%D0%BD%D0%BE%D0%BC%20%D0%B4%D0%BB%D1%8F%20%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BE%D0%B2.%3C/p%3E%3Cp%3E%D0%A0%D0%B0%D1%81%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B8%D0%BC%20%D1%81%D0%BB%D0%B5%D0%B4%D1%83%D1%8E%D1%89%D1%83%D1%8E%20%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D0%BD%D1%83%D1%8E%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%83:%3C/p%3E%3Cpre%3E%3Ccode%3Eclass%20Program%20{public:%20%20Program();%20void%20setup();%20void%20loop();private:%20int%20myFirstSampleVariable;%20int%20mySecondSampleVariable;};Program::Program()%20:%20myFirstSampleVariable(0),%20mySecondSampleVariable(0){}void%20Program::setup()%20{%20//your%20setup%20code%20goes%20here}void%20Program::loop()%20{%20//your%20loop%20code%20goes%20here}Program%20program;//your%20single%20globalvoid%20setup()%20{%20program.setup();}void%20loop()%20{%20program.loop();}%3C/code%3E%3C/pre%3E%3Cp%3E%D0%92%D0%BE%D0%B9%D0%BB%D0%B0,%20%D0%BC%D1%8B%20%D0%B8%D0%B7%D0%B1%D0%B0%D0%B2%D0%B8%D0%BB%D0%B8%D1%81%D1%8C%20%D0%BE%D1%82%20%D0%BF%D0%BE%D1%87%D1%82%D0%B8%20%D0%B2%D1%81%D0%B5%D1%85%20%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D0%BE%D0%B2.%20%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8,%20%D0%B2%20%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D1%85%20%D0%B2%D1%8B%20%D0%BD%D0%B0%D1%87%D0%BD%D0%B5%D1%82%D0%B5%20%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%BB%D1%8F%D1%82%D1%8C%20%D0%BB%D0%BE%D0%B3%D0%B8%D0%BA%D1%83%20%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F,%20%D0%B1%D1%83%D0%B4%D1%83%D1%82%20%3Ccode%3E%20Program%20::%20setup()%20%3C/code%3E%20%D0%B8%20%3Ccode%3E%20Program%20::%20loop()%20%3C/code%3E.%20%D0%AD%D1%82%D0%B8%20%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%20%D0%B8%D0%BC%D0%B5%D1%8E%D1%82%20%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%20%D0%BA%20%D0%BA%D0%BE%D0%BD%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D1%8B%D0%BC%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%BC-%D1%87%D0%BB%D0%B5%D0%BD%D0%B0%D0%BC-%D1%8D%D0%BA%D0%B7%D0%B5%D0%BC%D0%BF%D0%BB%D1%8F%D1%80%D0%B0%D0%BC%20%3Ccode%3E%20myFirstSampleVariable%20%3C/code%3E%20%D0%B8%20%3Ccode%3E%20mySecondSampleVariable%20%3C/code%3E,%20%D1%82%D0%BE%D0%B3%D0%B4%D0%B0%20%D0%BA%D0%B0%D0%BA%20%D1%82%D1%80%D0%B0%D0%B4%D0%B8%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9%20%3Ccode%3E%20setup()%20%3C/code%3E%20%D0%B8%20%3Ccode%3E%20loop()%20%3C/code%20%3E%20%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8%20%D0%BD%D0%B5%20%D0%B8%D0%BC%D0%B5%D1%8E%D1%82%20%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%B0,%20%D0%BF%D0%BE%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D1%83%20%D1%8D%D1%82%D0%B8%20%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%BE%D1%82%D0%BC%D0%B5%D1%87%D0%B5%D0%BD%D1%8B%20%D0%BA%D0%B0%D0%BA%20%D1%87%D0%B0%D1%81%D1%82%D0%BD%D1%8B%D0%B5.%20%D0%AD%D1%82%D0%B0%20%D0%BA%D0%BE%D0%BD%D1%86%D0%B5%D0%BF%D1%86%D0%B8%D1%8F%20%D0%BD%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D0%B5%D1%82%D1%81%D1%8F%20%3Ca%20href=) или скрытие
данных.

Обучение вам ООП и/или C ++ немного выходит за рамки ответа на
этот вопрос, поэтому я остановлюсь здесь.

Подводя итог: следует избегать глобализации, и почти всегда
можно резко уменьшить количество глобальных переменных. Также,
когда вы программируете для Arduino.

Самое главное, я надеюсь, что мой ответ несколько полезен для
вас :)



Глобальные переменные никогда не являются *злом* . Общее
правило против них - всего лишь костыль, позволяющий вам выжить
достаточно долго, чтобы получить опыт для принятия правильных
решений.

Что такое глобальная переменная, является неотъемлемым
предположением, что есть только одна вещь (неважно, говорим ли мы о
глобальном массиве или карте, которая может содержать несколько
вещей, которая все еще содержит предположение, что существует
только *один* такой список или сопоставление, а не несколько
независимых).

Поэтому, прежде чем использовать глобальный подход, вы хотите
спросить себя: возможно ли, что я когда-нибудь захочу использовать
более одной из этих вещей? Если он окажется правдивым по линии, вам
придется изменить код, чтобы не глобализовать эту вещь, и вы,
вероятно, узнаете, что другие части вашего кода зависят от этого
предположения об уникальности, поэтому вы также придется исправлять
их, и процесс становится утомительным и подверженным ошибкам. «Не
использовать глобалы» учат, потому что обычно это довольно
небольшая стоимость, чтобы избежать глобалов с самого начала, и это
позволяет избежать возможности заплатить большую стоимость
позже.

Но упрощающие предположения о том, что глобалы позволяют сделать
ваш код меньше, быстрее и использовать меньше памяти, потому что
ему не нужно обходить понятие о том, какую вещь он использует, не
нужно делать косвенность, не нужно рассмотрите возможность того,
что желаемая вещь, возможно, не существует, и т. д. Во встроенной
системе вы, скорее всего, будете ограничены размером кода и/или
процессорным временем и/или памятью, чем на компьютере, поэтому эти
сбережения могут иметь значение. И многие встроенные приложения
также имеют большую жесткость в требованиях - вы *знаете* ,
что ваш чип имеет только одно из определенных периферийных
устройств, пользователь не может просто подключить другой порт USB
или что-то в этом роде.

Еще одна распространенная причина для желания более чем одного
из уникальных объектов - тестирование взаимодействия между двумя
компонентами проще, когда вы можете просто передать экземпляр
тестирования какого-либо компонента функции, тогда как попытка
изменить поведение глобального компонента более сложное
предложение. Но тестирование во встроенном мире, как правило,
сильно отличается от других, поэтому это может не относиться к вам.
Насколько я знаю, у Ардуино нет тестовой культуры.

Итак, продолжайте использовать глобальные переменные, когда они
кажутся стоящими. Кодекс полиции не придет и не получит вас. Просто
знайте, что неправильный выбор может привести к гораздо большему
труду для вас в будущем, поэтому, если вы не уверены ...



> 
> Являются ли глобальные переменные злом в Arduino?
> 
> 
> 

ничто изначально не является злом, включая глобальные
переменные. Я бы охарактеризовал его как «необходимое зло» - он
может сделать вашу жизнь намного легче, но к ней следует подходить
с осторожностью.

> 
> Кроме того, какие практики я могу использовать для уменьшения
> числа глобалов?
> 
> 
> 

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