Зачем использовать переменную int для вывода, когда const int, enum или #define имеют гораздо больше смысла

Почему люди используют переменную для указания номера контакта, если контакт вряд ли изменится во время выполнения кода?

Много раз я видел int, используемый для определения вывода,

int led = 13;

при использовании const int

const int led = 13;

или enum или #define

#define LED 13

имеет гораздо больше смысла.

Это есть даже в учебниках на сайте Arduino, например, в первом учебнике, который запускает большинство людей, Blink.

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

Что касается памяти/обработки/вычислений, то это const int, enum или, если на то пошло, #define, что лучше, чем простой >int, т.е. занимает меньше памяти, хранится в другой памяти (Flash, EEPROM, SRAM), быстрее выполнение, быстрее компиляция?


Может показаться, что это дубликат Что лучше использовать #define или const int для констант?, но я задаюсь вопросом, почему люди используют переменные, и как улучшается производительность, когда они этого не делают, а не какой тип констант лучше.

, 👍23

Обсуждение

Потому что ужасное порождает ужасное. Большинство любителей не являются опытными программистами и поэтому обучают других любителей вредным привычкам., @Ignacio Vazquez-Abrams

В частности, что касается выводов, упрощенная форма основных функций API Arduino, таких как digitalWrite, не способствует правильному встроенному дизайну, то есть использованию масок и единого адреса памяти для всего порта., @crasic


4 ответа


Лучший ответ:

20
const int led = 13;

Это правильный метод. Или даже:

const byte led = 13;

Сколько у вас булавок?

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

Производительность будет выше при использовании const byte по сравнению с int, однако компилятор может быть достаточно умен, чтобы понять, что вы делаете.

Что вы можете сделать, так это мягко побудить людей использовать более эффективные методы, используя их в своем собственном коде.


Ответы на комментарии

  1. Комментатор предположил, что byte не является стандартным C. Это верно, однако это сайт Arduino StackExchange, и я считаю, что использование стандартных типов, предоставляемых Arduino IDE, приемлемо.

    В Arduino.h есть такая строка:

    typedef uint8_t byte;
    

    Обратите внимание, что это не совсем то же самое, что unsigned char. См. uint8_t против неподписанного символа и Когда uint8_t ≠ беззнаковый символ?.

  2. Другой комментатор предположил, что использование byte не обязательно улучшит производительность, поскольку числа, меньшие, чем int, будут повышены до int (см. Правила целочисленного продвижения, если вы хочу больше об этом).

    Однако в контексте идентификатора const компилятор в любом случае сгенерирует эффективный код. Например, дизассемблирование "blink" дает это в исходном виде:

    00000086 <loop>:
      86:   8d e0           ldi r24, 0x0D   ; 13
      88:   61 e0           ldi r22, 0x01   ; 1
      8a:   1b d1           rcall   .+566       ; 0x2c2 <digitalWrite>
    

    Фактически он генерирует тот же код, что и 13:

    • Это литерал.
    • Является ли #define
    • Является ли const int
    • Является ли константным байтом

Компилятор знает, когда он может поместить число в один регистр, а когда нет. Однако хорошей практикой является использование кода, указывающего ваше намерение. Создание const дает понять, что число не изменится, а использование byte (или uint8_t) дает понять, что вы ожидаете небольшое количество.


Непонятные сообщения об ошибках

Еще одна важная причина избегать #define — это сообщения об ошибках, которые вы получаете, если допустили ошибку. Рассмотрим этот скетч «мигания», в котором есть ошибка:

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- строка с ошибкой
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- строка с ошибкой
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- строка с ошибкой
  delay(1000);              
}

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

Blink.ino: In function ‘void setup()’:
Blink:4: error: expected primary-expression before ‘=’ token
Blink:4: error: expected primary-expression before ‘,’ token
Blink:4: error: expected `;' before ‘)’ token
Blink.ino: In function ‘void loop()’:
Blink:8: error: expected primary-expression before ‘=’ token
Blink:8: error: expected primary-expression before ‘,’ token
Blink:8: error: expected `;' before ‘)’ token
Blink:10: error: expected primary-expression before ‘=’ token
Blink:10: error: expected primary-expression before ‘,’ token
Blink:10: error: expected `;' before ‘)’ token

Вы смотрите на первую выделенную строку (строка 4) и даже не видите символа "=". Плюс линия выглядит нормально. Теперь совершенно очевидно, в чем проблема (= 13 заменяется на LED), но когда строка находится на 400 строк ниже в коде, это не так. очевидно, проблема в том, как определяется светодиод.

Я много раз видел, как люди на это попадались (в том числе и я).

,

_Сколько у вас контактов?_ это очень хороший вопрос, Ник, так как на большинстве плат их количество находится только в диапазоне десятков, а не сотен (т.е. больше 255), поэтому int является излишним... то есть до тех пор, пока Arduino наконец-то выпустила плату _Tera_... :-), @Greenonline

[C не имеет типа byte](http://stackoverflow.com/questions/20024690/is-there-byte-data-type-in-c). Вы имеете в виду «беззнаковый символ»., @Kevin

Производительность не обязательно будет выше при использовании byte вместо int, поскольку в большинстве контекстов целочисленные значения типов, меньших, чем int, повышаются до int., @Pete Becker

C не имеет байтового типа. Вы имеете в виду беззнаковый символ. - Мой ответ был в контексте Arduino, который имеет этот typedef uint8_t byte;. Так что для Arduino можно использовать byte., @Nick Gammon

Производительность не обязательно будет лучше при использовании byte вместо int - см. исправленный пост., @Nick Gammon


20

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

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

р>

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

Да, const предпочтительнее неконстантного и даже #define, потому что:

  • const (например, #define, в отличие от неконстантного) не выделяет ОЗУ
  • const (аналогично неконстантному, но в отличие от #define) придает значению явный тип

Особый интерес представляет второй пункт. Если специально не указано иное со встроенным приведением типов ((long)3), суффиксом типа (3L) или наличием десятичной точки (3.0), #define числа всегда будет целым числом, и все математические действия, выполняемые с этим значением, будут такими, как если бы оно было целым числом. В большинстве случаев это не проблема, но вы можете столкнуться с интересными сценариями при попытке #define значения, большего, чем может хранить целое число, например, #define COUNT 70000, а затем выполнить математическую операцию с другими значениями int. Используя const, вы сообщаете компилятору: «Это значение следует рассматривать как этот тип переменной», поэтому вместо этого вы должны использовать: const long count = 70000; и все будет работать так, как ожидалось.

Это также имеет дополнительный эффект: он проверяет тип при передаче значения по месту. Попробуйте передать const long функции, которая ожидает int, и она будет жаловаться на сужение диапазона переменных (или даже полностью не сможет скомпилироваться в зависимости от сценария). Сделайте это с помощью #define, и он просто молча будет выдавать вам неправильные результаты, и вы будете часами чесать голову.

,

Стоит отметить, что переменная const *может* требовать оперативной памяти, в зависимости от контекста, например, если она инициализируется с использованием возвращаемого значения из функции, отличной от constexpr., @Peter Bloomfield

Аналогично, const int foo = 13; bar(&foo); обязательно потребует от компилятора выделения фактической памяти для foo., @Ilmari Karonen

Если вы определяете макрос, который расширяется до значения, которое не помещается в int, компилятор рассматривает значение как имеющее наименьший тип, которому оно соответствует (правила по модулю относительно знакового и беззнакового). Если вы работаете в системе, где int имеет длину 16 бит, #define count 70000 приведет к тому, что count будет выглядеть как long, так же, как если бы он был определен как const long count = 70000; . Более того, если вы передадите любую из этих версий count функции, ожидающей int, любой нормальный компилятор будет относиться к ним одинаково., @Pete Becker

Я согласен с @PeteBecker - такая конструкция, как #define COUNT 70000, не усекается до целого числа, но компилятор рассматривает ее как тип, достаточно большой, чтобы хранить это число. Это правда, что когда вы используете COUNT, может быть неочевидно, что это не int, но вы все равно можете сказать то же самое о const long., @Nick Gammon

_"#define всегда будет целым числом"_ Это неправда. Вы берете правила целочисленных литералов и применяете их к макросам препроцессора. Это всё равно, что сравнивать яблоки и поп-музыку. Выражение COUNT в вашем примере перед компиляцией заменяется выражением 70000, тип которого определяется правилами литералов, точно так же, как 2, 13L или 4.0 определяются правилами литералов. . Тот факт, что вы используете #define для псевдонимов этих выражений, не имеет значения. Если хотите, вы можете использовать #define для псевдонимов произвольных фрагментов кода C., @Lightness Races in Orbit

Как следствие, ваш последний абзац тоже неверен. Похоже, вы в корне неправильно понимаете препроцессор. Жаль, потому что остальная часть ответа была великолепной!, @Lightness Races in Orbit

Я не понимаю, о чём ты там. Я ничего не применяю к макросам. Я применяю литеральные правила к **результатам раскрытия макроса** — именно поэтому я говорю о явном приведении типов и суффиксах типов внутри макросов., @Majenko


0

Мой ответ: они делают это, потому что это работает. Мне трудно не задать в ответе вопрос, например: «Почему это должно быть «неправильно»?»

,

Отличительной чертой хорошего программиста является то, что код всегда отражает его намерения., @Ignacio Vazquez-Abrams

Мы все еще говорим об Arduino, верно? ;), @linhartr22

Arduino уже имеет плохую репутацию в широком сообществе EE из-за посредственных и ужасных аппаратных проектов, предлагаемых сообществом. Разве нам не стоит попытаться наплевать на _что-то_?, @Ignacio Vazquez-Abrams

Думаю, я начал ценить подходы «достаточно», которые распространены в сообществе разработчиков Arduino. Большинство проектов не связаны с риском для жизни или финансов, так какая разница, использует ли Blink.ino int или const int? Те, кто придерживается этого и набирается опыта, поймут, когда это важно, а когда нет., @linhartr22

«Большинство проектов не связаны с риском для жизни или финансов…» В этом нет ничего удивительного. Кто *захочет* задействовать Arduino там, где есть хоть какой-то риск, после изучения сообщества в целом., @Ignacio Vazquez-Abrams

Это «неправильно» не потому, что это не работает в какой-то конкретной ситуации, а потому, что по сравнению с тем, чтобы делать это «правильно», существует больше ситуаций, в которых это _не_ работает. Это делает код хрупким; изменения в коде могут вызвать загадочные сбои, которые съедают время отладки. Проверка типов и сообщения об ошибках компилятора помогут вам обнаружить подобные ошибки раньше, а не позже., @cjs


2

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

,