Arduino - это странный гибрид, в котором во встроенном мире используется некоторая функциональность C ++ - традиционно среда C. Действительно, большая часть кода Arduino очень похожа на Си.
C традиционно использует #define
s для констант. Есть ряд причин для этого:
- Вы не можете установить размеры массива, используя
const int
. - Вы не можете использовать в
const int
качестве меток оператора case (хотя это работает в некоторых компиляторах) - Вы не можете инициализировать
const
с другимconst
.
Вы можете проверить этот вопрос на StackOverflow для большего количества рассуждений.
Итак, что мы должны использовать для Arduino? Я склоняюсь к этому #define
, но вижу, что некоторые используют код, const
а некоторые используют смесь.
programming
c++
coding-standards
Cybergibbons
источник
источник
#define
очевидный выбор. Мой пример в названии аналоговых контактов - как A5. Для него нет подходящего типа, который можно было бы использовать какconst
единственный, поэтому единственный вариант - использовать a#define
и позволить компилятору подставить его в качестве ввода текста перед интерпретацией значения.Ответы:
Важно отметить, что в C и C ++
const int
они не ведут себя одинаково, поэтому на самом деле некоторые из возражений против этого, которые были упомянуты в первоначальном вопросе и в обширном ответе Питера Блумфилдса, недопустимы:const int
константы являются значениями времени компиляции и могут использоваться для установки ограничений массива, таких как метки регистра и т. Д.const int
константы не обязательно занимают какое-либо хранилище. Если вы не берете их адрес или не объявляете их внешними, они обычно будут существовать во время компиляции.Однако для целочисленных констант часто может быть предпочтительнее использовать (именованный или анонимный)
enum
. Мне часто это нравится, потому что:const int
(точно так же, как безопасность типов в C ++ 11).Таким образом, в идиоматической программе на C ++ нет никаких оснований для
#define
определения целочисленной константы. Даже если вы хотите оставаться совместимым с C (из-за технических требований, потому что вы заблуждаетесь в старой школе или потому, что люди, с которыми вы работаете, предпочитают это), вы все равно можете использоватьenum
и должны это делать, а не использовать#define
.источник
const int
. Для более сложных типов вы правы в том, что хранилище может быть выделено, но даже в этом случае вам вряд ли будет хуже, чем с#define
.РЕДАКТИРОВАТЬ: microtherion дает превосходный ответ, который исправляет некоторые из моих пунктов здесь, особенно об использовании памяти.
Как вы определили, в определенных ситуациях вы вынуждены использовать a
#define
, потому что компилятор не допуститconst
переменную. Точно так же в некоторых ситуациях вы вынуждены использовать переменные, например, когда вам нужен массив значений (т.е. вы не можете иметь массив#define
).Тем не менее, существует много других ситуаций, в которых не существует единственного «правильного» ответа. Вот несколько рекомендаций, которым я бы следовал:
Безопасность типов
С общей точки зрения программирования
const
переменные обычно предпочтительнее (где это возможно). Основной причиной этого является безопасность типов.#define
(Препроцессор макро) непосредственно копирует буквенное значение в каждое место в коде, что делает каждый независимо использование. Это может гипотетически привести к неоднозначности, потому что тип может быть решен по-разному в зависимости от того, как / где он используется.const
Переменная только когда - либо один тип, который определяется его декларации, и решены в ходе инициализации. Он часто требует явного приведения, прежде чем он будет вести себя по-другому (хотя существуют различные ситуации, когда его можно безопасно неявно повышать по типу). По крайней мере, компилятор может (если он настроен правильно) выдавать более надежное предупреждение при возникновении проблемы с типом.Возможным обходным путем для этого является включение явного приведения или суффикса типа в
#define
. Например:Такой подход может потенциально вызвать проблемы синтаксиса в некоторых случаях, в зависимости от того, как он используется.
Использование памяти
В отличие от вычислений общего назначения, память имеет преимущество при работе с чем-то вроде Arduino. Использование
const
переменной против a#define
может повлиять на то, где данные хранятся в памяти, что может заставить вас использовать одну или другую.const
переменные (как правило) будут храниться в SRAM вместе со всеми другими переменными.#define
часто хранятся в программном пространстве (флэш-память) вместе с самим эскизом.(Обратите внимание, что есть разные вещи, которые могут повлиять на то, как и где что-то хранится, например, конфигурация компилятора и оптимизация.)
SRAM и Flash имеют разные ограничения (например, 2 КБ и 32 КБ соответственно для Uno). Для некоторых приложений достаточно просто запустить SRAM, поэтому полезно перенести некоторые вещи во Flash. Обратное также возможно, хотя, вероятно, менее распространено.
PROGMEM
Можно получить преимущества безопасности типов и одновременно хранить данные в программном пространстве (Flash). Это делается с помощью
PROGMEM
ключевого слова. Он не работает для всех типов, но обычно используется для массивов целых чисел или строк.Общая форма, приведенная в документации, выглядит следующим образом:
Строковые таблицы немного сложнее, но в документации есть полная информация.
источник
Для переменных определенного типа, которые не изменяются во время выполнения, обычно можно использовать любой из них.
Для цифровых номеров выводов, содержащихся в переменных, может работать любой из них, например:
Но есть одно обстоятельство, когда я всегда использую
#define
Он предназначен для определения аналоговых номеров выводов, поскольку они являются буквенно-цифровыми.
Конечно, вы можете жестко закодировать номера выводов как
a2
,a3
и т. Д. Во всей программе, и компилятор будет знать, что с ними делать. Затем, если вы поменяете булавки, то каждое использование должно быть изменено.Более того, я всегда хотел бы, чтобы мои определения выводов были наверху, все в одном месте, поэтому возникает вопрос, какой тип
const
будет подходить для вывода, определенного какA5
.В этих случаях я всегда использую
#define
Пример делителя напряжения:
Все установочные переменные находятся в самом верху, и никогда не будет изменений в значении,
adcPin
кроме как во время компиляции.Не беспокойтесь о том, что тип
adcPin
. И никакая дополнительная RAM не используется в двоичном файле для хранения константы.Компилятор просто заменяет каждый экземпляр
adcPin
на строкуA5
перед компиляцией.Существует интересная ветка форума Arduino, в которой обсуждаются другие способы решения:
#define vs. const переменная (форум Arduino)
Excertps:
Замена кода:
Код отладки:
Определение
true
иfalse
как логическое значение для экономии оперативной памятиМногое из этого сводится к личным предпочтениям, однако ясно, что
#define
оно более универсально.источник
const
не будет использовать больше оперативной памяти, чем a#define
. А для аналоговых выводов я бы определил их какconst uint8_t
, хотяconst int
без разницы.const
самом деле не использует больше оперативной памяти [...] до тех пор, пока она не будет фактически использована ». Вы упустили мою мысль: большую часть времени aconst
не использует RAM, даже когда она используется . Затем « это многопроходный компилятор ». Самое главное, это оптимизирующий компилятор. По возможности константы оптимизируются в непосредственные операнды .