Лучше ли использовать константы #define или const int для констант?

Arduino - нечетный гибрид, где некоторые встроенные функции C ++ используются во встроенном мире - традиционно среда C. Действительно, много кода Arduino очень похоже на C.

C традиционно использовал #define s для констант. Для этого есть несколько причин:

  1. Вы не можете установить размеры массива с помощью const int.
  2. Вы не можете использовать const int как метки оператора case (хотя это работает в некоторых компиляторах)
  3. Вы не можете инициализировать const с помощью другого const.

Вы можете проверить этот вопрос на StackOverflow для получения дополнительных аргументов.

Итак, что мы должны использовать для Arduino? Я склоняюсь к #define, но я вижу некоторый код с помощью const, а некоторые - с помощью сочетания.

22 голоса | спросил Cybergibbons 28 FebruaryEurope/MoscowbFri, 28 Feb 2014 13:46:51 +0400000000pmFri, 28 Feb 2014 13:46:51 +040014 2014, 13:46:51

3 ответа


19

Важно отметить, что const int выполняет не поведение одинаково на C и на C ++, поэтому на самом деле несколько возражений против него, на которые ссылались в исходный вопрос и в обширном ответе Питера Блумфилда недействительны:

  • В C ++ константы const int - это значения времени компиляции и могут использоваться для установки пределов массива, как метки меток и т. д.
  • Константы const int не обязательно занимают какое-либо хранилище. Если вы не возьмете их адрес или не объявите им extern, они, как правило, просто будут иметь время компиляции.

Однако для целочисленных констант часто может быть предпочтительнее использовать (именуемый или анонимный) enum. Мне часто нравится это, потому что:

  • Он обратно совместим с C.
  • Это почти безопасно для типа const int (каждый бит как безопасный тип в C ++ 11).
  • Он обеспечивает естественный способ группировки связанных констант.
  • Вы даже можете использовать их для некоторого количества элементов управления пространством имен.

Итак, в идиоматической программе на C ++ нет никакой причины использовать #define для определения целочисленной константы. Даже если вы хотите оставаться совместимым с C (из-за технических требований, потому что вы учите его старой школе или потому, что люди, с которыми вы работаете, предпочитают это так), вы все равно можете использовать enum и должны сделайте это, а не используйте #define.

ответил microtherion 28 FebruaryEurope/MoscowbFri, 28 Feb 2014 19:05:36 +0400000000pmFri, 28 Feb 2014 19:05:36 +040014 2014, 19:05:36
7

РЕДАКТИРОВАНИЕ: микроэлемент дает отличный ответ, который исправляет некоторые из моих моментов здесь, особенно об использовании памяти.


Как вы уже определили, существуют определенные ситуации, когда вы вынуждены использовать #define, потому что компилятор не разрешает переменную const. Аналогичным образом, в некоторых ситуациях вы вынуждены использовать переменные, например, когда вам нужен массив значений (т. Е. Вы не можете иметь массив #define).

Тем не менее, существует много других ситуаций, когда нет необходимости в одном правильном ответе. Вот некоторые рекомендации, которые я буду придерживаться:

Тип безопасности
Из общей точки программирования, как правило, предпочтительны переменные const (где это возможно). Основной причиной этого является безопасность типов.

A #define (макрос препроцессора) непосредственно копирует буквальное значение в каждое место в коде, делая каждое использование независимым. Это может гипотетически привести к двусмысленности, потому что тип может быть разрешен по-разному в зависимости от того, как /где он используется.

A const переменная - это только один тип, который определяется его объявлением и разрешен во время инициализации. Это часто требует явного броска, прежде чем он будет вести себя по-другому (хотя бывают различные ситуации, когда он может безопасно быть неявным образом продвигаемым по типу). По крайней мере, компилятор может (если он настроен правильно) выдавать более надежное предупреждение при возникновении проблемы с типом.

Возможным обходным путем для этого является включение явного выражения или суффикса типа в #define. Например:

#define THE_ANSWER (int8_t) 42
#define NOT_QUITE_PI 3.14f

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

Использование памяти
В отличие от вычислений общего назначения, память, очевидно, стоит того, чтобы иметь дело с чем-то вроде Arduino. Использование переменной const по сравнению с #define может повлиять на то, где данные хранятся в памяти, что может заставить вас использовать один или другой.

  • const переменные будут (обычно) храниться в SRAM вместе со всеми другими переменными.
  • Литеральные значения, используемые в #define, часто сохраняются в программном пространстве (флэш-памяти) вместе с самим эскизом.

(Обратите внимание, что существуют различные вещи, которые могут точно влиять на то, как и где что-то хранится, например, конфигурация и оптимизация компилятора.)

SRAM и Flash имеют разные ограничения (например, 2 КБ и 32 КБ соответственно для Uno). Для некоторых приложений довольно просто выходить из SRAM, поэтому может быть полезно переместить некоторые вещи во Flash. Обратное также возможно, хотя, вероятно, и менее распространено.

PROGMEM
Вы можете получить преимущества безопасности типов, одновременно сохраняя данные в программном пространстве (Flash). Это делается с помощью ключевого слова PROGMEM. Он не работает для всех типов, но обычно используется для массивов целых чисел или строк.

Общий вид, приведенный в документации , выглядит следующим образом:

dataType variableName [] PROGMEM = {dataInt0, dataInt1, dataInt3 ...};

Строковые таблицы немного сложнее, но документация содержит полную информацию.

ответил Peter Bloomfield 28 FebruaryEurope/MoscowbFri, 28 Feb 2014 14:40:05 +0400000000pmFri, 28 Feb 2014 14:40:05 +040014 2014, 14:40:05
1

Для переменных указанного типа, которые не изменяются во время выполнения, обычно можно использовать.

Для цифровых номеров пинов, содержащихся в переменных, они могут работать, например:

const int ledPin = 13;

Но есть одно обстоятельство, когда я всегда использую #define

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

Конечно, вы можете жестко закодировать номера контактов в виде a2, a3 и т. д. все на протяжении всей программы, и компилятор будет знать, что с ними делать. Затем, если вы меняете булавки, каждое использование нужно будет изменить.

Кроме того, мне всегда нравятся определения моих контактов вверху всего в одном месте, поэтому возникает вопрос, какой тип const был бы подходящим для вывода, определенного как A5.

В таких случаях я всегда использую #define

Разделитель напряжения Пример:

//
//read12 Считывает напряжение 12-вольтовой батареи
//
//SDsolar 8/8/18
//
#define adcInput A5 //Выход делителя напряжения поступает на аналоговый A5
float R1 = 120000,0; //R1 для входа делителя напряжения от внешнего 0-15 В
float R2 = 20000,0; //R2 для вывода делителя напряжения для АЦП
float vRef = 4.8; //9V на Vcc проходит через регулятор
float vTmp, vIn;
int value;
,
,
void setup () {
,
//разрешить АЦП стабилизировать
значение = analogRead (adcPin); задержка (50); значение = analogRead (adcPin); задержка (50);
значение = analogRead (adcPin); задержка (50); значение = analogRead (adcPin); задержка (50);
значение = analogRead (adcPin); задержка (50); значение = analogRead (adcPin);
,
void loop () {
,
,
  значение = analogRead (adcPin);
  vTmp = значение * (vRef /1024.0);
  vIn = vTmp /(R2 /(R1 + R2));
 ,
 ,

Все параметры настройки находятся справа вверху и никогда не будет изменений в значении adcPin, кроме времени компиляции.

Не беспокойтесь о том, какой тип adcPin. И никакая дополнительная оперативная память не используется в двоичном файле для хранения константы.

Компилятор просто заменяет каждый экземпляр adcPin строкой A5 перед компиляцией.


Есть интересный форум Arduino Forum, в котором обсуждаются другие способы решения:

#define vs. const variable (форум Arduino)

Excertps:

Подстановка кода:

#define FOREVER для (;;)

НАВСЕГДА
 {
 if (serial.available ()> 0)
   ...
 }

Отладочный код:

#ifdef DEBUG
 #define DEBUG_PRINT (x) Serial.println (x)
#else
 #define DEBUG_PRINT (x)
#endif

Определение true и false как Boolean для сохранения ОЗУ

Вместо использования `const bool true = 1;` и того же для `false`

#define true (boolean) 1
#define false (boolean) 0

Многие из них сводятся к личным предпочтениям, однако ясно, что #define более универсален.

ответил SDsolar 26 MarpmMon, 26 Mar 2018 20:58:07 +03002018-03-26T20:58:07+03:0008 2018, 20:58:07

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132