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

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

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

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

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

20
@LookAlterno: «Я избегаю» и «вы не можете» - это разные вещи.
добавлено автор James Hopkin, источник
Глобальные переменные на Arduino менее вредны для ПК. Я видел людей, объявляющих переменную цикла, i, как глобальную переменную. TBH Я стараюсь избегать их, но у них есть место в управлении памятью.
добавлено автор Thomas Myron, источник
@LookAlterno - Самая большая проблема с глобальными переменными - это плохие программисты. Вы должны передать каждое значение в качестве аргумента (попробуйте сохранить его меньше 5), поскольку это разделяет ваш код и позволяет повторно использовать его. Скопировать и вставить функции вместе в программы очень просто, но только в том случае, если вы избегаете глобалов как можно больше. Он также предотвращает монолитные функции, если вы передаете 20 аргументов в функцию линии 2000, тогда вы должны реорганизовать ее.
добавлено автор Thomas Myron, источник
Фактически, некоторые встроенные программисты запрещают локальные переменные и вместо этого требуют глобальные переменные (или статические переменные с функцией). Когда программы малы, это может облегчить анализ его как простой конечной машины; потому что переменные никогда не перезаписывают друг друга (т. е. как они выполняют стек и кучу выделенных переменных).
добавлено автор Hououin Kyouma, источник
@LookAlterno Err, вы не можете писать классы в Ardunio, так как это просто C ++ с странными макросами и библиотеками? Если это так, не каждая переменная глобальна. И даже в C обычно считается предпочтительным переключение переменных (возможно, внутри структур) на функции, а не на глобальные переменные. Может быть, менее удобно для небольшой программы, но она обычно окупается по мере того, как программа становится все более сложной.
добавлено автор sarath, источник
В Arduino вы не можете избежать глобальных переменных. Каждое объявление переменной вне сферы действия функции/метода является глобальным. Итак, если вам нужно делиться значениями между функциями, они должны быть глобальными, если вы не хотите передавать каждое значение в качестве аргумента.
добавлено автор user31481, источник
@Muzer. Я использую классы C ++ только для разработки библиотеки, а также для более крупных программ. Я избегаю C ++ по причинам.
добавлено автор user31481, источник
добавлено автор Brian, источник
@LookAlterno Это обыденный трюизм C/C ++ (несмотря на все пространства имен), что совсем не характерно для Arduino, и, как вы сами указали, никоим образом не приводит к выводу о невозможности избежать глобальных переменных; они могут и часто должны. Если ответ на этот вопрос может отличаться, если платформа Arduino не находится на уровне языка, а скорее в шаблонах проектирования, которые имеют смысл на платформе и желают ли они сделать глобальные глобалы.
добавлено автор Not that Charles, источник
@Muzer huh, всегда думал, что это обработка. TIL
добавлено автор Sonic Splasher, источник
Статические и глобальные переменные обеспечивают преимущество использования памяти во время компиляции. С ардуином, имеющим очень ограниченную память, это может быть преимуществом. Для новичков довольно легко израсходовать доступную память и испытать непревзойденные сбои.
добавлено автор mgpugne, источник
Привет всем: Это стало намного популярнее, чем ожидалось. За последние 9 месяцев я многому научился и хотел бы сделать это полезным для «другого меня», насколько это возможно. Пойдут ли люди, если я добавлю это сообщение со ссылками на учебные пособия/идеи, которые помогли мне в качестве новичка улучшить мой код? Я до сих пор не уверен на 100% на тему обмена стопкой, поэтому я подумал, что попрошу 1-го.
добавлено автор cocco, источник

8 ответы

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

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

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

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

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

26
добавлено
Правильно, я слишком быстро переместился и соединил некоторые комментарии по вопросу о неприменении классов.
добавлено автор JaanusSiim, источник
Классы могут быть выделены в стек. По общему признанию, неплохо ставить большие экземпляры в стек, но все же.
добавлено автор JaanusSiim, источник
@JAB Все, что может быть выделено в стек.
добавлено автор Majenko, источник
В этом случае я бы, вероятно, определил их в loop() (возможно, в качестве static , если мне нужно, чтобы они сохраняли свое значение в итерациях) и передавали их через параметры функции по цепочке вызовов.
добавлено автор Majenko, источник
Это один из способов или передать его как ссылку: void foo (int & var) {var = 4; } и foo (n); - n теперь 4.
добавлено автор Majenko, источник
Одна из причин, по которой я слышал, чтобы избежать глобальных переменных, имеет больше общего с системой, отличной от Arduino, глобальные переменные могут быть открыты внешними или вне самой программы. Этот оператор C ++ утверждает, что это лучше всего. «Язык C не имеет ключевого слова global. Переменные, объявленные вне функции, имеют« область файла », что означает, что они видны внутри файла». Это предоставляет переменное пространство для изменения вне области программы перед запуском, как в случае статики, где пространство переменных файла можно редактировать до запуска, создавая потенциальные риски безопасности
добавлено автор Clems, источник
Хорошо, я думаю, что смогу это сделать. Если у меня есть (например) static var n , определенный в loop() . Если я передаю n в foo (var) и в foo (var) , я установил n = [новое значение] . Мне нужно вернуть return [новое значение] и иметь n = [новое значение] в loop() ?
добавлено автор cocco, источник
Хорошо, что, если это то, что я использую только в loop() (или в нескольких функциях, вызываемых в loop() )? было бы лучше настроить их по-другому, чем определять их в начале?
добавлено автор cocco, источник
Факт отсутствия связи между setup() и loop() сам по себе является отличной причиной для использования более стандартного инструментария и хорошего старого main() или _main() .
добавлено автор Megatron, источник

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

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

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

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

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

16
добавлено
Нет. static предназначен только тогда, когда вам нужно сохранить значение. Если первое, что вы делаете в функции, задает значение переменной в текущее время, нет необходимости сохранять старое значение. Все, что статично делает в этой ситуации, напрасно тратит память.
добавлено автор Phil Hannent, источник
Это очень хорошо округленный ответ, выражающий оба момента в пользу и скептицизм в отношении глобальных переменных. +1
добавлено автор Not that Charles, источник
Хорошо, спасибо за разъяснение.
добавлено автор cocco, источник
ОК! Я не знал о static , если у меня есть переменная, которая используется только в одной функции, но получает новое значение каждый раз, когда вызывается функция (например, var = millis() ), должен ли я сделать это static ?
добавлено автор cocco, источник

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

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

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

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

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

5
добавлено
В AVR глобальные блоки находятся в ОЗУ, тогда как большинство нестатических локалей выделяются для регистров CPU, что делает их доступ быстрее.
добавлено автор Sprogz, источник
Проблема не в нахождении объявления; способность быстро понять код важна, но в конечном счете является вторичной по отношению к тому, что он делает - и там реальная проблема заключается в поиске того, какой varmint, в котором неизвестная часть вашего кода может использовать глобальную декларацию, чтобы сделать странную вещи с объектом, который вы оставили в открытом для всех, чтобы делать все, что они хотят.
добавлено автор Not that Charles, источник

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

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

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

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

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

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

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

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

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

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

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

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

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

4
добавлено
Если вы считаете, что goto - зло, проверьте код Linux. Возможно, они так же злы, как try ... catch блоки при использовании как таковые.
добавлено автор nreich, источник
Я ничего не знаю об Arduinos, но занимаюсь большим количеством настольных и серверных разработок. Существует одно допустимое использование (IMHO) для goto s, и это должно вырваться из вложенных циклов, это намного чище и легче понять, чем альтернативы.
добавлено автор sagelynaive, источник

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

Их можно избежать? Почти всегда да. Проблема с 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. Если вы еще не знакомы с инкапсуляция данных или скрытие данных.

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

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

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

3
добавлено
Ну, это все еще глобальное состояние, к которому можно получить доступ в любом месте программы, вы просто получаете его через Program :: instance (). Setup() вместо globalProgram.setup() . Включение связанных глобальных переменных в одно пространство/struct/namespace может быть полезным, особенно если они нужны только для нескольких связанных функций, но одноэлементный шаблон ничего не добавляет. Другими словами, static Program p; имеет глобальное хранилище и static Program & instance() имеет глобальный доступ, который равен тому же, что и просто Program globalProgram; .
добавлено автор Tomas Andrle, источник
Синглтон действительно глобальный, просто одетый в дополнительном запутанном способе. Он имеет те же недостатки.
добавлено автор Tomas Andrle, источник
Вы можете определить свой собственный main() в эскизе, если хотите. Вот что выглядит на складе: github.com/arduino/Arduino/blob/1.8.3/hardware/arduino/avr/…
добавлено автор per1234, источник
@patstew Не возражаете ли вы объяснить мне, как вы чувствуете, что у него такие же недостатки? По-моему, это не так, поскольку вы можете использовать инкапсуляцию данных в свою пользу.
добавлено автор gebeyaw, источник
@ per1234 Спасибо! Я определенно не эксперт по Arduino, но я полагаю, что мое первое предложение также может работать.
добавлено автор gebeyaw, источник
@patstew Я так не думал об этом. Я думаю ты прав. Я изменю свой пост. Это также облегчит понимание.
добавлено автор gebeyaw, источник
@Graham Мне любопытно узнать, о какой дисциплине вы говорите? Я думаю, что у меня есть идея, о чем вы говорите, но дисциплина не собирается создавать «хорошо инкапсулированный код». Шаблон доступа/мутатора иногда приходит с небольшой производительностью, в зависимости от того, как вы настроите свой код, согласился. Однако многие дорогостоящие операции могут быть оптимизированы компилятором.
добавлено автор gebeyaw, источник
Как давний встроенный кодер, я довольно регулярно должен обучать людей из чугунного правила «Global Variables Bad». Передача аргументов always делает код медленнее. На ПК это немаловажно, но во встроенном мире это все еще очень важно. Шаблон доступа/мутатора - это способ гарантировать инкапсуляцию звука за счет времени обработки. Вполне возможно написать хорошо инкапсулированный код с глобальными переменными - но вам нужно применить дисциплину к тому, как вы ее разрабатываете, и четко указать, как другие люди должны ее использовать.
добавлено автор Maxim Krizhanovsky, источник

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

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

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

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

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

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

2
добавлено

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

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

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

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

0
добавлено
Если вы используете функции-обертки для доступа к глобальным переменным, вы можете также поместить свои переменные в эти функции.
добавлено автор nreich, источник
Анархическая электроника
Анархическая электроника
1 510 участник(ов)

[около] электронные темы. без переходов на личности, стен стикеров, политики, непрошенной рекламы и всякого такого Основной чат у нас в @ru_electronics Общий информационный канал @ru_electronics_feed

Про электронику
Про электронику
1 461 участник(ов)

QA чат для решения вопросов, связанных с электроникой. без стикеров, непрошенной рекламы и игр в русский форум оформляйте вопрос в одно сообщение вопросы со словом «кто» игнорируются don't ask for ask Правила http://telegra.ph/ru-electronics-rulz-11-11

Embedded Group
Embedded Group
873 участник(ов)

Все про Embedded и электронику. Осторожно, бывают нотификейшены. #вопросподелу - Для поиска вопросов и ответов #devtools - фотки рабочих железок Работа: @rabotaembedded http://embedded.group http://vk.com/embedded_space

Hardware & Radio
Hardware & Radio
155 участник(ов)

Разговоры об электронике, микроконтроллерах, низкоуровневом программировании, реверс-инжиниринге, FPGA, квадрокоптерах, 3D-печати, Software Defined Radio, любительском радио, и всяком таком.

ARDUINO [RU]
ARDUINO [RU]
60 участник(ов)

Обсуждение Электронного конструктора Arduino. Проблемы и их решения. Ссылки на интересные статьи и проекты. ВК: https://vk.com/arduino_esp Realtek: http://vk.com/rtl8711 Чаты: IOT https://t.me/ProIOT esp8266 https://t.me/Proesp8266