Я относительно новичок в программировании, и многие из лучших практик кодирования, которые я читаю, утверждают, что очень мало веских причин для использования глобальной переменной (или что лучший код вообще не имеет глобальных переменных).
Я сделал все возможное, чтобы помнить об этом, когда писал программу для создания интерфейса Arduino с SD-картой, общался с компьютером и запускал контроллер мотора.
В настоящее время у меня есть 46 глобальных переменных для примерно 1100 строк кода «начального уровня» (ни одна строка не содержит более одного действия). Это хорошее соотношение или мне стоит больше его уменьшать? Кроме того, какие методы я могу использовать, чтобы еще больше сократить количество глобалов?
Я спрашиваю об этом здесь, потому что меня особенно интересуют лучшие практики кодирования на продуктах Arduino, а не компьютерное программирование в целом.
источник
Ответы:
Они не являются злом как таковые, но они, как правило, чрезмерно используются там, где нет веских причин использовать их.
Есть много раз, когда глобальные переменные являются выгодными. Тем более, что программирование Arduino под капотом сильно отличается от программирования на ПК.
Самым большим преимуществом глобальных переменных является статическое распределение. Особенно с большими и сложными переменными, такими как экземпляры классов. Динамическое распределение (использование и
new
т. Д.) Осуждается из-за нехватки ресурсов.Кроме того, вы не получаете одно дерево вызовов, как в обычной программе на Си (одна
main()
функция, вызывающая другие функции) - вместо этого вы фактически получаете два отдельных дерева (setup()
вызывая функции, затемloop()
вызывая функции), что означает, что иногда глобальные переменные являются Единственный способ достичь своей цели (т. е. если вы хотите использовать ее в обоихsetup()
иloop()
).Так что нет, они не злые, и на Arduino они используются лучше и лучше, чем на ПК.
источник
loop()
(или в нескольких вызванных функцияхloop()
)? было бы лучше настроить их иначе, чем определять их в начале?static
если бы я нуждался в них, чтобы они сохраняли свое значение на протяжении итераций) и передавал их через параметры функции по цепочке вызовов.void foo(int &var) { var = 4; }
иfoo(n);
-n
теперь 4.Очень сложно дать однозначный ответ, не увидев ваш реальный код.
Глобальные переменные не являются злом, и они часто имеют смысл во встроенной среде, где вы обычно имеете большой аппаратный доступ. У вас есть только четыре UARTS, только один порт I2C и т. Д. Поэтому имеет смысл использовать глобальные переменные для переменных, привязанных к конкретным аппаратным ресурсам. И действительно, Arduino библиотека ядра делает это:
Serial
,Serial1
и т.д. являются глобальными переменными. Кроме того, переменная, представляющая глобальное состояние программы, обычно является глобальной.Не о цифрах. Правильный вопрос, который вы должны себе задать, заключается в том, имеет ли смысл иметь его в глобальном масштабе для каждого из этих глобалов.
Тем не менее, 46 глобальных кажется мне немного выше. Если некоторые из них содержат постоянные значения, квалифицируйте их как
const
: компилятор обычно оптимизирует их хранилище. Если какая-либо из этих переменных используется только внутри одной функции, сделайте ее локальной. Если вы хотите, чтобы его значение сохранялось между вызовами функции, определите его какstatic
. Вы также можете уменьшить количество «видимых» глобальных переменных, сгруппировав переменные внутри класса и имея один глобальный экземпляр этого класса. Но делайте это только тогда, когда есть смысл собрать вещи вместе. Создание большогоGlobalStuff
класса ради единственной глобальной переменной не поможет сделать ваш код более понятным.источник
static
ли у меня переменная, которая используется только в одной функции, но я получаю новое значение каждый раз, когда вызывается функция (какvar=millis()
), должен ли я сделать этоstatic
?static
только для того, когда вам нужно сохранить значение. Если первое, что вы делаете в функции, это устанавливаете значение переменной в текущее время, то нет необходимости сохранять старое значение. Все, что делает статика в этой ситуации, тратит память.Основной проблемой с глобальными переменными является обслуживание кода. При чтении строки кода легко найти объявление переменных, переданных в качестве параметра или объявленных локально. Не так просто найти объявление глобальных переменных (часто для этого требуется и IDE).
Когда у вас много глобальных переменных (40 уже много), становится трудно иметь явное имя, которое не слишком длинное. Использование пространства имен - это способ прояснить роль глобальных переменных.
Плохой способ имитировать пространства имен в C:
На процессорах Intel или ARM доступ к глобальным переменным медленнее, чем к другим переменным. Это, вероятно, наоборот на Arduino.
источник
Хотя я бы не стал использовать их при программировании для ПК, для Arduino у них есть некоторые преимущества. Большинство, если это уже было сказано:
Кроме того, в некоторых случаях, особенно с точки зрения производительности, может быть полезно использовать глобальные переменные вместо создания элементов при необходимости:
источник
Как и во всем (кроме gotos, которые действительно злые) глобалы имеют свое место.
Например, если у вас есть отладочный последовательный порт или файл журнала, в который вы должны иметь возможность записи из любого места, часто имеет смысл сделать его глобальным. Точно так же, если у вас есть некоторая критическая часть информации о состоянии системы, то сделать ее глобальным часто является самым простым решением. Нет смысла иметь значение, которое вы должны передать каждой функции в программе.
Как уже говорили другие 46, кажется, много всего для более чем 1000 строк кода, но, не зная деталей того, что вы делаете, трудно сказать, перешли ли вы на их использование или нет.
Однако для каждого глобального задайте себе несколько важных вопросов:
Является ли имя ясным и конкретным, чтобы я случайно не попытался использовать то же имя где-нибудь еще? Если нет, измените имя.
Должно ли это когда-либо меняться? Если нет, то подумайте о том, чтобы сделать его постоянным.
Должно ли это быть видимым везде или только глобально, чтобы значение поддерживалось между вызовами функций? Поэтому подумайте о том, чтобы сделать его локальным для функции и использовать ключевое слово static.
Неужели все будет плохо, если это будет изменено фрагментом кода, когда я не буду осторожен? например, если у вас есть две связанные переменные, скажем, имя и идентификационный номер, которые являются глобальными (см. предыдущее примечание об использовании global, когда вам нужна информация почти везде), то если одна из них будет изменена без других неприятных вещей, может произойти. Да, вы могли бы просто быть осторожными, но иногда полезно немного усилить осторожность. Например, поместите их в другой файл .c, а затем определите функции, которые устанавливают их одновременно и позволяют читать их. Затем вы только включаете функции в связанный заголовочный файл, таким образом, остальная часть вашего кода может получить доступ к переменным только через определенные функции и поэтому не может испортить ситуацию.
- update - я только что понял, что вы спрашивали о конкретной передовой практике Arduino, а не об общем кодировании, и это скорее общий ответ на кодирование. Но, честно говоря, нет большой разницы, хорошая практика - хорошая практика. Структура
startup()
иloop()
структура Arduino означает, что в некоторых ситуациях вам приходится использовать глобалы чуть больше, чем другие платформы, но это не сильно меняет ситуацию, вы всегда стремитесь к лучшему, что можете сделать в рамках ограничений платформы, несмотря ни на что платформа есть.источник
goto
s, и оно состоит в том, чтобы вырваться из вложенных циклов, это намного чище и проще для понимания, чем альтернативы.goto
это зло, посмотрите код Linux. Возможно, они так же злы, какtry...catch
блоки, когда используются как таковые.Они злые? Может быть. Проблема с глобальными переменными состоит в том, что они могут быть доступны и изменены в любой момент времени любой выполняемой функцией или частью кода без ограничений. Это может привести к ситуациям, которые, скажем, трудно отследить и объяснить. Поэтому желательно свести к минимуму количество глобалов, если возможно, вернуть их к нулю.
Можно ли их избежать? Почти всегда да. Проблема с Arduino заключается в том, что они заставляют вас использовать этот двухфункциональный подход, при котором они предполагают вас
setup()
и васloop()
. В этом конкретном случае у вас нет доступа к области действия функции вызова этих двух функций (вероятноmain()
). Если бы вы имели, вы могли бы избавить себя от всех глобальных и использовать вместо этого местных жителей.Изобразите следующее:
Это, вероятно, более или менее так выглядит основная функция программы Arduino. Переменные, которые вам нужны как в функции, так
setup()
и вloop()
функции, будут предпочтительно объявляться внутри области действияmain()
функции, а не в глобальной области действия. Затем их можно сделать доступными для двух других функций путем передачи их в качестве аргументов (при необходимости используя указатели).Например:
Обратите внимание, что в этом случае вам также необходимо изменить сигнатуру обеих функций.
Поскольку это может быть неосуществимо и не желательно, я вижу в действительности только один способ удалить большинство глобалов из программы Arduino без изменения принудительной структуры программы.
Если я правильно помню, вы вполне можете использовать C ++ при программировании для Arduino, а не C. Если вы еще не знакомы (пока) с ООП (объектно-ориентированным программированием) или C ++, может потребоваться некоторое привыкание и некоторые чтение.
Моим предложением было бы создать класс Program и создать единый глобальный экземпляр этого класса. Класс должен рассматриваться как проект для объектов.
Рассмотрим следующий пример программы:
Вуаля, мы избавились от почти всех глобалов. Функции , в которых вы бы начать добавлять логику приложения будет правильным
Program::setup()
иProgram::loop()
функции. Эти функции имеют доступ к переменным - членам экземпляра конкретныхmyFirstSampleVariable
и вmySecondSampleVariable
то время как традиционныеsetup()
иloop()
функции не имеют доступа , поскольку эти переменные были отмечены класса приватным. Эта концепция называется инкапсуляцией или сокрытием данных.Обучение ООП и / или C ++ немного выходит за рамки ответа на этот вопрос, поэтому я на этом остановлюсь.
Подводя итог: глобалы следует избегать, и почти всегда можно резко сократить количество глобалов. Также, когда вы программируете для Arduino.
Самое главное, я надеюсь, что мой ответ несколько полезен для вас :)
источник
Program::instance().setup()
вместоglobalProgram.setup()
. Помещение связанных глобальных переменных в одно пространство классов / структур / имен может быть полезным, особенно если они нужны только нескольким связанным функциям, но шаблон синглтона ничего не добавляет. Другими словами,static Program p;
имеет глобальное хранилище иstatic Program& instance()
глобальный доступ, что равносильно простомуProgram globalProgram;
.Глобальные переменные никогда не являются злом . Общее правило против них - это просто опора, позволяющая вам выжить достаточно долго, чтобы получить опыт и принимать лучшие решения.
Что такое глобальная переменная, это врожденное предположение, что существует только одна вещь (не имеет значения, говорим ли мы о глобальном массиве или карте, которые могут содержать несколько вещей, которые все еще содержат предположение, что существует только одна вещь). один такой список или отображение, а не несколько независимых).
Поэтому, прежде чем использовать глобальный, вы хотите спросить себя: возможно ли, что я когда-нибудь захочу использовать больше, чем одну из этих вещей? Если это окажется правдой, вам придется изменить код, чтобы не глобализировать эту вещь, и вы, вероятно, обнаружите, что другие части вашего кода зависят от этого предположения уникальности, поэтому вы Придется исправлять и их, и процесс становится утомительным и подверженным ошибкам. Преподавание «Не используйте глобалы» объясняется тем, что обычно избегать глобальных затрат с самого начала довольно просто, и это исключает возможность того, что впоследствии придется заплатить большую цену.
Но упрощающие допущения, которые допускают глобальные переменные, также делают ваш код меньше, быстрее и используют меньше памяти, потому что он не должен передавать понятия о том, что он использует, не должен делать косвенные действия, не должен рассмотреть возможность того, что нужной вещи, возможно, не существует, и т. д. Во встроенной системе вы, скорее всего, будете ограничены размером кода и / или процессорного времени и / или памяти, чем на ПК, поэтому такая экономия может иметь значение. И многие встроенные приложения также имеют более жесткие требования - вы знаете, что у вашего чипа есть только одно периферийное устройство, пользователь не может просто подключить другое к USB-порту или к чему-то другому.
Еще одна распространенная причина, по которой вам нужно больше, чем что-то уникальное, - это тестирование - тестирование взаимодействия между двумя компонентами проще, когда вы можете просто передать тестирующий экземпляр какого-либо компонента в функцию, тогда как попытка изменить поведение глобального компонента более хитрое предложение. Но тестирование во встроенном мире, как правило, сильно отличается от других, поэтому это может не относиться к вам. Насколько я знаю, у Arduino нет тестовой культуры вообще.
Так что продолжайте и используйте глобальные переменные, когда они кажутся стоящими. Кодекс полиции не придет и не заберет вас. Просто знайте, что неправильный выбор может привести к гораздо большей работе для вас в будущем, так что если вы не уверены ...
источник
ничто по своей сути не зло, включая глобальные переменные. Я бы охарактеризовал его как «необходимое зло» - оно может значительно облегчить вашу жизнь, но к нему следует подходить с осторожностью.
используйте функции-оболочки для доступа к вашим глобальным переменным. так что вы, по крайней мере, управляете этим с точки зрения области видимости.
источник