Почему плохие глобальные переменные в однопоточном встроенном приложении
Большинство возражений, которые я вижу по поводу использования глобальных переменных, имеют смысл, так как они относятся к проблемам нескольких потоков, безопасности потоков и т. Д.
Но в небольшом однопоточном, не ОС случае, какие у вас возражения? В моем случае я пишу свою встроенную систему на "С", если это имеет значение. Я также единственный разработчик продукта.
Почему устранение глобальных переменных сделало мой код лучше?
(Прочитав несколько ответов, я понимаю, что также должен был указать, что в этой системе нет динамического выделения памяти (например, malloc). Вся память статически выделяется во время компиляции.)
14 ответов
Это не будет.
Две фундаментальные проблемы с глобальными переменными - это просто загромождение пространства имен и тот факт, что "никто" не "контролирует" их (таким образом, потенциальные коллизии и конфликты с несколькими потоками).
"Глобальность плохая", как и почти любая другая компьютерная программируемая идиома, является руководством, а не жестким и быстрым правилом. Когда создаются такие "правила", лучше всего просто принять правило наизусть, чтобы понять обстоятельства и мотивы создания правила. Не принимай их вслепую.
В вашем случае вы, похоже, поняли природу вашей системы и аргументы вокруг правила и решили, что в этом случае оно не применимо. Ты прав, это не так.
Так что не беспокойся об этом.
Глобальные переменные не обязательно плохие, так же как макросы не обязательно плохие, а обогащенный уран не обязательно плохой. Пока вы принимаете осознанное решение о плюсах и минусах того или иного выбора дизайна, у вас должно быть все в порядке.
Некоторые аргументы против глобальных переменных:
- Они нарушают хороший объектно-ориентированный дизайн
- Они затрудняют юнит-тестирование вашего кода, так как вы не можете протестировать отдельные блоки кода без установки всех глобальных переменных, которые код ожидает увидеть
- Они увеличивают сцепление в вашем коде: действия в одном блоке кода могут непредсказуемым образом повлиять на вещи в другом блоке кода через общую глобальную переменную
Некоторые аргументы для глобальных переменных:
- Они позволяют легко разделить один ресурс между многими функциями
- Они могут сделать код проще для чтения
Если в вашем проекте глобальные переменные имеют смысл и могут быть использованы для того, чтобы сделать ваш код более простым для чтения или поддержки, без необходимости настраивать себя на случайные ошибки и испытывать головные боли, тогда обязательно используйте их.
Я пишу много кода на C для встроенных микроконтроллеров и постоянно использую глобальные переменные. В такой жесткой системе глобальные переменные имеют смысл. Я знаю о потенциальных недостатках, но я проанализировал плюсы и минусы, и я пишу свой код, чтобы защититься от основных ошибок.
Итог: нет жестких и быстрых правил. Просто примите лучшее решение для вашего конкретного проекта или платформы, основываясь на лучшей информации, которую вы имеете.
Вот хорошая статья, которая объясняет, почему глобальные переменные плохие
Почему глобальных переменных следует избегать, когда они не нужны?
Нелокальность. Исходный код легче всего понять, когда область его отдельных элементов ограничена. Глобальные переменные могут быть прочитаны или изменены любой частью программы, что затрудняет их запоминание или рассуждение о каждом возможном использовании. Отсутствие контроля доступа или проверки ограничений - глобальная переменная может быть получена или установлена любой частью программы, и любые правила, касающиеся ее использования, могут быть легко нарушены или забыты.
Неявная связь - Программа со многими глобальными переменными часто имеет тесную связь между некоторыми из этих переменных и связь между переменными и функциями. Группировка связанных элементов в единые блоки обычно приводит к улучшению программ.
Проблемы с выделением памяти. В некоторых средах существуют схемы выделения памяти, которые усложняют распределение глобалов. Это особенно верно в языках, где "конструкторы" имеют побочные эффекты, отличные от распределения (поскольку в этом случае вы можете выразить небезопасные ситуации, когда два глобальных элемента взаимно зависят друг от друга). Кроме того, при динамическом связывании модулей может быть неясно, имеют ли разные библиотеки свои собственные экземпляры глобалов или глобальные глобальные ресурсы являются общими.
Тестирование и ограничение - источник, использующий глобальные переменные, несколько сложнее протестировать, поскольку нельзя легко установить "чистую" среду между запусками. В более общем смысле, источник, который использует глобальные сервисы любого рода, которые явно не предоставлены этому источнику, трудно проверить по той же причине.
Добавление глобалов действительно легко. Легко привыкнуть объявлять их. Это гораздо быстрее, чем думать о хорошем дизайне.
Глобальные переменные не так плохи, как вы думаете, их следует избегать всякий раз, когда они не нужны. Глобальные переменные могут найти хорошее применение для переменной, которая будет использоваться во всей программе, помня о том, что вы всегда должны отслеживать, где эта переменная принимает изменения; но для переменных, которые, как правило, используются только в ограниченных частях программы, это хороший повод, чтобы избежать ее глобализации.
Потому что это минимизирует сцепление. Ваша система может быть маленькой сейчас, но если вы продолжите работать, может оказаться, что это не так.
Глобальные переменные необходимы в небольшом встроенном приложении, написанном на C. Например, вам нужно использовать глобальную переменную для передачи информации между подпрограммой обработки прерываний и другим модулем. Вот несколько советов, которые помогут вам эффективно использовать глобальные переменные во встроенных приложениях:
Сделайте различие между статическими и глобальными переменными. Статические переменные могут использоваться из всех функций в одном и том же C-файле. Они являются эквивалентом закрытых членов в классе C++. В Си вы должны выполнять работу компилятора самостоятельно. Используйте ключевое слово static, чтобы избежать случайного использования переменной за пределами модуля и сделать ее видимой. Вы можете добавить префикс переменной к имени модуля.
Следуйте соглашению об именах глобальных переменных (используется многими C-файлами). Сделайте ясно, что они глобальны.
Если вам нужно много глобальных переменных, рассмотрите возможность их объединения в структуру.
При необходимости используйте ключевое слово volatile. Это необходимо, если глобальная переменная изменена ISR.
Код, который использует глобальные переменные, сложнее поддерживать. Поскольку сопровождающий должен найти каждое использование переменной в системе, прежде чем он сможет точно знать, что делает переменная. Поскольку это замедляет обслуживание, его следует избегать, насколько это возможно. Вот и все
Это вопрос сферы. У нас есть разработчики, которые любят создавать глобальные переменные для создания локальных концепций. Это усложняет отслеживание использования этих переменных, поэтому легче допускать ошибки при их использовании.
Вы также можете держать свой кошелек на переднем крыльце, но тогда у вас будет гораздо меньше контроля над тем, кто получает к нему доступ.
Тем не менее, есть некоторые допустимые варианты использования глобальной переменной, но, как и любое другое правило, заслуживающее внимания. Это исключение, а не нормальный случай. Их существующее указывает на то, что у них есть применение.
Если вы решите использовать глобальную переменную, постарайтесь комментировать их и давать им хорошие имена. Меня действительно беспокоит, когда люди создают глобальные переменные, такие как "bool bIsFound;"
Мы считаем необходимым оценить каждую новую глобальную переменную в обзоре кода и посмотреть, есть ли лучший подход.
Возможно , в вашем случае глобальные переменные неплохие. С другой стороны, они вам нужны? Что вы получаете, используя их?
Даже в вашем случае глобальные переменные затрудняют определение состояния приложения. Это может помешать отладке и может привести к незначительным ошибкам.
Глобальные переменные также усложняют тестирование. Они добавляют зависимости от внешнего кода, которых вы можете избежать во время тестирования.
Но, наконец, глобальные переменные трудно избежать при программировании на C. На более продвинутом языке глобальные переменные просто бессмысленны в большинстве случаев. Использование их просто не делает ваш код более понятным.
В C существует множество случаев, когда глобальные переменные являются просто лучшим, самым простым и чистым решением.
Так что в вашем случае принимайте это на индивидуальной основе. Вы, вероятно, в конечном итоге получите некоторые глобальные переменные, но не делайте переменную глобальной, если не уверены, что это действительно лучшее решение, не только сейчас, но и через год, когда вам придется отлаживать код или создавать приложение. многопоточный.
Короткий ответ заключается в том, что глобальные переменные исторически были источником тонких ошибок. Но затем (надевая мою функциональную шляпу программиста) все переменные были источниками тонких ошибок.
По сути, глобальная переменная означает, что вы не можете смотреть на кусок кода, используя значение переменной, и знать, что он собирается делать, не зная, что каждый другой фрагмент кода, устанавливающий значение этой переменной, делал в прошлом.
Одна из причин, которая приходит на ум, - это будущее. Возможно, вы сейчас являетесь единственным разработчиком вашего продукта, но, возможно, другие будут поддерживать его позже. Конечно, это может быть не так в вашем конкретном случае.
Хотя ваш продукт может быть небольшим и аккуратным прямо сейчас - когда использование глобальных переменных не усложняет общий дизайн или не ухудшает читаемость - кто скажет, что продукт не станет намного больше или будет включен в другой продукт? И не дай бог другому сопровождающему / разработчику придётся разобраться в использовании глобальных переменных.
Конечно, вы можете решить использовать глобальные переменные сейчас, и когда проект становится все более сложным, вернитесь и переписайте его части, но зачем навязывать эту дополнительную работу себе? Вы с большей вероятностью (если вы вообще помните глобальные переменные) решите, что выполнять эту работу - слишком много усилий, и позволить дизайну уйти: в этот момент у вас проблемы!
Итак, избавьте себя от головной боли и задумайтесь о будущем. Необходимая работа сейчас, вероятно, облегчит жизнь в будущем.
Если это не убедило вас, представьте, что вы разработчики, которые работали над этим проектом и обнаружили, что их код был признан ошибочным и виновным в недопустимых методах программирования перед Верховным судом: код ошибки багги-алкотестера отражает важность проверки исходного кода.
Проблема заключается в том, чтобы отследить, какой бит вашего последнего кода изменил состояние глобального. Как правило, вы хотите, чтобы переменные находились в наименьшей возможной области видимости, чтобы вам было легче их рассуждать.
Хранение большинства переменных локально и уменьшение их области действия помогает продвигать лучшие практики программирования. Это также снижает риск ошибочного изменения переменной и необходимости отслеживать через функции в поисках непредвиденных или неочевидных последствий. Я считаю, что это также облегчает чтение и поддержку кода, поскольку большинство переменных, относящихся к определенному разделу кода, определены и инициализированы поблизости. В противном случае я, кажется, постоянно возвращаюсь к разделу кода, определяющему все golbals. Я использую некоторые глобальные переменные, но в основном для передачи информации о конфигурации в субмодули.
С глобальными переменными часто трудно понять, где и когда они обновляются и какой фрагмент кода обновляет эту переменную. Нет контроля над тем, кто может его обновить или каким образом обновляется переменная.
Нетрудно отследить, является ли ваше приложение маленьким, но чем оно больше, и чем больше у вас глобальных переменных, тем больше код приложения выглядит как спагетти.
Короче говоря - это становится кошмаром обслуживания и отладки.
Когда-то все переменные были глобальными и это был бардак (как в старом Бейсике).
Представьте себе, что вы используете for(i...) с глобальным i...
function a() {
for(i=0;i<n:i++) b();
}
function b() {
for(i=0;i<m:i++) n=i;
}
Вы видите проблемы? Когда вы пишете a(), вы можете не знать о b() и обратном порядке. Внутри b вы измените те же I и n a. Это относится ко всем переменным, а не только к тем, которые используются в циклах. Хорошей практикой является сохранение как можно большей части материалов локальными.
Тем не менее, во встроенных системах с ограниченными ресурсами, если они используются «как база данных» для сохранения некоторых данных сеанса с использованием понятных поясняющих имен и уменьшенного их количества, обычной практикой является избегание написания кода для передачи значений здесь и там.