Навіщо використовувати змінну int для штифта, коли const int, enum або #define має набагато більше сенсу


24

Чому люди використовують змінну, щоб вказати номер PIN-коду, коли PIN-код навряд чи зміниться протягом усього виконання коду?

Багато разів я бачу, intяк використовується для визначення шпильки,

int led = 13;

при використанні а const int

const int led = 13;

або enum, або#define

#define LED 13

має набагато більше сенсу.

Навіть у навчальних посібниках на сайті Arduino, наприклад, перший підручник, який працює більшість людей, Блимає .

Я читав десь, що const intвважається за краще #define. Чому цього не рекомендується спочатку, а не дозволяти людям розвивати шкідливі звички з самого початку? Я помітив це деякий час назад, але останнім часом він почав мене дратувати, звідси і питання.

Пам'ять / обробки / обчислення мудрі є const int, enumабо на те пішло #define, краще , ніж рівнини int, тобто займає менше пам'яті, яка зберігається в інший пам'яті (Flash, EEPROM, SRAM), більш швидке виконання, швидше скомпілювати?


Це може здатися дублікатом Чи краще використовувати #define або const int для констант? , але я торкаюсь питання, чому люди використовують змінні та як покращується продуктивність, коли їх немає, а не який тип константи є кращим.


9
Тому що страшне народжує жахливе. Більшість любителів не є досвідченими програмістами, тому навчають інших любителів шкідливим звичкам.
Ігнасіо Васкес-Абрамс

1
Зокрема, штифтами спрощена форма основних функцій API ардуїно, таких як 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 vs неподписаний char та Коли isint8_t igned неподписаний char? .

  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це зайве ... тобто поки Ардуїно нарешті не вийде з дошки Tera ... :-)
Greenonline

2
C не має byteтипу . Ви маєте на увазі unsigned char.
Кевін

Продуктивність не обов'язково буде кращою byteзамість int, оскільки в більшості контекстів ціле значення має типи, менші, ніж intдо них int.
Піт Бекер

1
C doesn't have a byte type. You mean unsigned char.- Моя відповідь була в контексті Ардуїно, який має це typedef uint8_t byte;. Так що для Arduino, використання byteнормально.
Нік Гаммон

Performance won't necessarily be better with byte instead of int- див. змінену публікацію.
Нік Гаммон

19

Як справедливо стверджує Ігнасіо, це в основному тому, що вони не знають краще. І вони не знають краще, тому що люди, які навчали їх (або ресурси, якими вони користувалися під час навчання), не знали краще.

Значна частина коду та навчальних посібників Arduino написана людьми, які ніколи не навчалися програмуванню і дуже "самоучки" з ресурсів людьми, які самі сильно навчаються без належної підготовки з програмування.

Багато фрагментів коду підручника, який я бачу по всьому місцю (і особливо тих, які доступні лише у відео YouTube YouTube --- urgh), були б оцінкою провалу, якби я відзначав їх на іспиті.

Так, a constє кращим над non-const і навіть над a #define, тому що:

  • A const(як #define, на відміну від non-const) не виділяє жодної оперативної пам'яті
  • A const(як не-const, але на відміну від a #define) надає значення явного типу

Другий момент викликає особливий інтерес. Якщо спеціально не сказано інше із вбудованим типом лиття ( (long)3) або суфіксом типу ( 3L) або наявністю десяткової крапки ( 3.0), #defineчисло числа завжди буде цілим числом, і вся математика, виконана на цьому значенні, буде як би ціле число. Більшу частину часу це не проблема, але ви можете зіткнутися з цікавими сценаріями, намагаючись #defineзберегти значення, яке перевищує ціле число, наприклад, #define COUNT 70000а потім виконати математичну операцію з іншими intзначеннями на ньому. Використовуючи constкоманду a, ви маєте сказати компілятору "Це значення має трактуватися як цей тип змінної" - так ви б замість цього використали: 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виглядатимете так long, як якщо б це було визначено як const long count = 70000;. Крім того, якщо ви передасте будь-яку з цих версій countфункції, яка очікує int, будь-який розумний компілятор буде ставитися до них однаково.
Піт Бекер

1
Я погоджуюся з @PeteBecker - така конструкція #define COUNT 70000не врізається в int, але компілятор розглядає її як тип, достатньо великий, щоб утримувати це число. Це правда, що це може бути не очевидним, коли ви використовуєте, COUNTщо це не int, але ви могли б сказати те ж саме про const longвсе-таки.
Нік Гаммон

2
"#define завжди буде цілим числом" Це неправда. Ви приймаєте правила цілих літералів і застосовуєте їх до макросів препроцесора. Це як порівняння яблук та поп-музики. Вираз COUNTв вашому прикладі замінюється перед компіляцією з виразом 70000, яке має тип , визначений за правилами литералов, так само , як 2і 13Lчи 4.0визначається правилами литералов. Те, що ви використовуєте #defineдля псевдонімів цих виразів, не має значення. #defineЯкщо ви хочете, ви можете використовувати для псевдоніму довільні фрагменти коду С.
Гонки легкості з Монікою

2

Як новачок у Ардуїно на 2 тижні, я підхопив загальну ідею того, що Ардуїно займають непрограмісти. Більшість розглянутих нами ескізів, в тому числі на сайті Arduino, показують повний брак порядку, а ескізи, які не працюють, і ледве цілісний коментар на очах. Діаграми потоків не існують, а "Бібліотеки" - це немодерні перешкоди.


0

Моя відповідь - це вони роблять, бо це працює. Мені важко не ставити запитання у своїй відповіді, наприклад "чому це має бути" неправильно "?"


3
Однією відмітною ознакою хорошого програміста є те, що код завжди відображає їх наміри.
Ігнасіо Васкес-Абрамс

1
Ми все ще говоримо про Ардуїноса, правда? ;)
linhartr22

3
У Arduino вже є поганий представник у більшій спільноті EE через посередньо-страшні апаратні конструкції, викладені громадою. Чи не повинні ми намагатися щось сказати ?
Ігнасіо Васкес-Абрамс

2
"Більшість проектів не можуть спричинити ризик для життя або фінансів ..." Не дивно. Хто б хотів залучати Ардуїно там, де є шанс ризику після перегляду спільноти.
Ігнасіо Васкес-Абрамс

2
Це "неправильно" не тому, що він не працює в одній конкретній ситуації, а тому, що в порівнянні з тим, як робити це "правильно", існує більше ситуацій, в яких він не працює. Це робить код крихким; зміни коду можуть спричинити таємничі збої, які втрачають час налагодження. Повідомлення про перевірку типу та повідомлення про помилки компілятора є для того, щоб допомогти вам знайти ці помилки раніше, ніж пізніше.
Керт Дж. Сампсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.