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

24

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

Много раз я видел, 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 для констант? , но я обращаюсь к вопросу о том, почему люди используют переменные и как улучшается производительность, когда они этого не делают, а не то, какой тип константы лучше.

Greenonline
источник
9
Потому что ужасное порождает ужасное. Большинство любителей не являются опытными программистами и поэтому учат других любителей вредным привычкам.
Игнасио Васкес-Абрамс
1
В частности, с помощью выводов, упрощенная форма основных функций API Arduino, таких как digitalWrite, не поощряет правильное встроенное проектирование, то есть использование масок и одного адреса памяти для всего порта
сбой

Ответы:

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. Другой комментатор предположил, что использование байта не обязательно улучшит производительность, потому что числа intбудут меньше, чем будут повышаться int(см. Правила продвижения по целочисленным значениям, если вы хотите узнать больше об этом).

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

    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

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


Смущающие сообщения об ошибках

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

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- line with error
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- line with error 
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- line with error
  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
2
С не имеет byteтипа . Вы имеете в виду unsigned char.
Кевин
byteВместо этого производительность не обязательно будет лучше int, поскольку в большинстве случаев целочисленное значение с типами меньше, чем intпредлагается int.
Пит Беккер
1
C doesn't have a byte type. You mean unsigned char.- Мой ответ был в контексте Arduino, который имеет это typedef uint8_t byte;. Так что для Arduino, использование byteв порядке.
Ник Гэммон
Performance won't necessarily be better with byte instead of int- см. исправленный пост.
Ник Гэммон
19

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

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

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

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

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

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

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

Маженко
источник
7
Стоит отметить, что для constпеременной может потребоваться оперативная память, в зависимости от контекста, например, если она инициализируется с использованием возвращаемого значения из функции non-constexpr.
Питер Блумфилд
Точно так const int foo = 13; bar(&foo);же определенно потребует, чтобы компилятор выделил фактическую память для foo.
Илмари Каронен
3
Если вы определяете макрос, который расширяется до значения, которое не помещается в intкомпиляторе, то оно обрабатывает значение как имеющее наименьший тип, в который оно будет помещаться (по модулю правил для подписанного и неподписанного). Если вы работаете в системе с int16 битами, #define count 70000результат будет countвыглядеть как a long, как если бы он был определен как const long count = 70000;. Кроме того, если вы передадите любую из этих версий countожидающей функции int, любой здравомыслящий компилятор будет обращаться с ними одинаково.
Пит Беккер
1
Я согласен с @PeteBecker - конструкция типа like #define COUNT 70000не усекается в int, но компилятор обрабатывает его как тип, достаточно большой, чтобы содержать это число. Это правда, что когда вы используете, это может быть неочевидно, если COUNTэто не int, но вы const longвсе равно можете сказать то же самое .
Ник Гэммон
2
«#define всегда будет целым числом» Это не так. Вы берете правила целочисленных литералов и применяете их к макросам препроцессора. Это как сравнивать яблоки и поп-музыку. Выражение COUNTв вашем примере заменяется перед компиляцией с выражением 70000, которое имеет тип , определенный по правилам литералов, так же , как 2и 13Lили 4.0определяется правилами литералов. Тот факт, что вы используете #defineпсевдонимы этих выражений, не имеет значения. Вы можете использовать #defineдля псевдонимов произвольные куски кода C, если хотите.
Легкость гонок с Моникой
2

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

Джим
источник
0

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

linhartr22
источник
3
Отличительной чертой хорошего программиста является то, что код всегда отражает их намерения.
Игнасио Васкес-Абрамс
1
Мы все еще говорим об Arduinos, верно? ;)
linhartr22
3
Arduino уже имеет плохую репутацию в более широком сообществе EE из-за посредственных и ужасных конструкций оборудования, представленных сообществом. Разве мы не должны постараться о чем-то наплевать ?
Игнасио Васкес-Абрамс
2
«Большинство проектов не связано с риском для жизни или финансов ...». Кто хочет привлечь Arduino , где есть какой -то шанс риска после того, как смотреть на общество в целом.
Игнасио Васкес-Абрамс
2
Это «неправильно» не потому, что оно не работает в одной конкретной ситуации, а потому, что, по сравнению с «правильным», есть больше ситуаций, в которых это не работает. Это делает код хрупким; изменения в коде могут вызвать загадочные сбои, которые отнимают время отладки. Проверки типов и сообщения об ошибках компилятора помогут вам обнаружить такие ошибки раньше, а не позже.
Курт Дж. Сэмпсон