Perché usare una variabile int per un pin quando const int, enum o #define ha molto più senso


24

Perché le persone usano una variabile per specificare un numero pin quando è improbabile che il pin cambi durante l'esecuzione del codice?

Molte volte vedo un intessere utilizzato per una definizione pin,

int led = 13;

quando l'uso di a const int

const int led = 13;

o enum, o#define

#define LED 13

ha molto più senso.

È persino nei tutorial sul sito di Arduino, ad esempio il primo tutorial che la maggior parte delle persone esegue, Blink .

Ho letto da qualche parte che const intè preferito #define. Perché questo non è incoraggiato fin dall'inizio, piuttosto che consentire alle persone di sviluppare cattive abitudini, fin dall'inizio? L'ho notato qualche tempo fa, ma recentemente ha iniziato a irritarmi, da qui la domanda.

Memoria / elaborazione / calcolo saggio è una const int, enumo per quella materia #define, meglio di una pianura int, cioè occupa meno memoria, memorizzato nella memoria diversa (Flash, EEPROM, SRAM), l'esecuzione più veloce, più veloce per compilare?


Questo può sembrare un duplicato di È meglio usare #define o const int per le costanti? , ma sto affrontando la domanda sul perché le persone usano le variabili e su come le prestazioni migliorano quando non lo fanno, piuttosto che quale tipo di costante è migliore.


9
Perché terribile genera terribile. La maggior parte degli hobbisti non sono programmatori esperti e quindi insegnano ad altri hobbisti cattive abitudini.
Ignacio Vazquez-Abrams,

1
Con i pin in particolare, la forma semplicistica delle funzioni di base dell'API arduino come digitalWrite non incoraggia il corretto design incorporato, ovvero l'uso di maschere e un unico indirizzo di memoria per l'intera porta
crasic

Risposte:


20
const int led = 13;

Questo è il metodo corretto. O anche:

const byte led = 13;

Quanti pin hai?

Alcuni tutorial non sono stati sottoposti al controllo di qualità che avrebbero potuto avere.

Le prestazioni saranno migliori usando const byte, rispetto a intcomunque il compilatore potrebbe essere abbastanza intelligente da capire cosa stai facendo.

Quello che puoi fare è incoraggiare delicatamente le persone a utilizzare tecniche più efficienti utilizzandole nel tuo codice.


Risposte ai commenti

  1. Un commentatore ha suggerito che bytenon è lo standard C. Questo è corretto, tuttavia si tratta di un sito StackExchange di Arduino e credo che l'utilizzo di tipi standard forniti dall'IDE di Arduino sia accettabile.

    In Arduino.h c'è questa linea:

    typedef uint8_t byte;

    Si noti che questo non è esattamente lo stesso di unsigned char. Vedi uint8_t vs char senza segno e Quando uint8_t ≠ char senza segno? .

  2. Un altro commentatore ha suggerito che l'uso del byte non migliorerà necessariamente le prestazioni, perché i numeri più piccoli di quelli intche verranno promossi int(vedi Regole di promozione di numeri interi se vuoi di più su questo).

    Tuttavia, nel contesto di un identificatore const , il compilatore genererà comunque un codice efficiente. Ad esempio, lo smontaggio di "battito di ciglia" dà questo nella forma originale:

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

    In effetti genera lo stesso codice sia che 13:

    • È letterale
    • È un #define
    • È un const int
    • È un const byte

Il compilatore sa quando può contenere un numero in un registro e quando non può. Tuttavia è buona norma utilizzare la codifica che indica il tuo intento . Renderlo constchiarisce che il numero non cambierà e renderlo byte(o uint8_t) chiarire che ci si aspetta un piccolo numero.


Messaggi di errore confusi

Un altro motivo importante per evitare #defineè i messaggi di errore che ricevi se commetti un errore. Considera questo schizzo "lampeggiante" che presenta un errore:

#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);              
}

In apparenza sembra OK, ma genera questi messaggi di errore:

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

Guarda la prima linea evidenziata (linea 4) e non vedi nemmeno il simbolo "=". Inoltre, la linea sembra a posto. Ora è abbastanza ovvio quale sia il problema ( = 13viene sostituito LED), ma quando la linea è 400 righe più in basso nel codice, non è ovvio che il problema riguarda il modo in cui il LED è definito.

Ho visto persone innamorarsi di questo molte volte (incluso me stesso).


Quanti pin hai? è un ottimo punto Nick, dato che la maggior parte delle schede ha solo una gamma di decine, non centinaia (cioè maggiore di 255), quindi intè eccessivo ... cioè fino a quando Arduino non esce finalmente con la scheda Tera ... :-)
Greenonline

2
C non ha un bytetipo . Intendi unsigned char.
Kevin,

Le prestazioni non saranno necessariamente migliori con byteinvece di int, poiché nella maggior parte dei contesti, valore intero con tipi più piccoli di quelli a cui intviene promosso int.
Pete Becker,

1
C doesn't have a byte type. You mean unsigned char.- La mia risposta è stata nel contesto di Arduino, che ha questo typedef uint8_t byte;. Quindi per un Arduino, usare byteè OK.
Nick Gammon

Performance won't necessarily be better with byte instead of int- vedi post modificato.
Nick Gammon

19

Come giustamente afferma Ignacio, è fondamentalmente perché non lo sanno meglio. E non sanno meglio perché le persone che hanno insegnato loro (o le risorse che hanno usato durante l'apprendimento) non lo sapevano meglio.

Gran parte del codice e dei tutorial di Arduino sono scritti da persone che non hanno mai avuto alcuna formazione in programmazione e sono molto "autodidatti" dalle risorse di persone che sono autodidatta senza una formazione adeguata in programmazione.

Molti dei frammenti di codice tutorial che vedo intorno al luogo (e specialmente quelli che sono disponibili solo all'interno dei video di YouTube --- urgh) sarebbero un segno di fallimento se li stessi contrassegnando in un esame.

Sì, a constè preferito su una non const e persino su a #define, perché:

  • A const(come a #define, a differenza di un non const) non alloca alcuna RAM
  • A const(come un non const, ma diversamente da a #define) fornisce al valore un tipo esplicito

Il secondo punto è di particolare interesse. Salvo diversamente specificato con il type-casting incorporato ( (long)3) o un suffisso di tipo ( 3L) o la presenza di un punto decimale ( 3.0), un #definedi un numero sarà sempre un numero intero e tutta la matematica eseguita su quel valore sarà come se fosse un numero intero. Il più delle volte non è un problema, ma è possibile imbattersi in scenari interessanti quando si tenta di #definememorizzare un valore maggiore di un numero intero, ad esempio, #define COUNT 70000quindi eseguire un'operazione matematica con altri intvalori su di esso. Usando un constsi arriva a dire al compilatore "Questo valore deve essere trattato come questo tipo di variabile" - quindi dovresti invece usare: const long count = 70000;e tutto funzionerebbe come previsto.

Ha anche l'effetto knock-on che controlla il tipo quando passa il valore intorno al luogo. Prova a passare const longa una funzione che si aspetta un inte si lamenterebbe di restringere l'intervallo di variabili (o addirittura non riuscire a compilare completamente a seconda dello scenario). Fallo con un #definee continuerebbe silenziosamente a darti i risultati sbagliati e ti lascerebbe a grattarti la testa per ore.


7
Vale la pena notare che una constvariabile può richiedere RAM, a seconda del contesto, ad esempio se viene inizializzata utilizzando il valore restituito da una funzione non constexpr.
Peter Bloomfield,

Allo stesso modo, const int foo = 13; bar(&foo);richiederà sicuramente al compilatore di allocare memoria effettiva per foo.
Ilmari Karonen,

3
Se si definisce una macro che si espande in un valore che non si adatta a un intcompilatore, il valore viene trattato come se avesse il tipo più piccolo in cui si adatterà (regole del modulo tra firmato e non firmato). Se sei su un sistema con int16 bit, #define count 70000sembrerà countun long, proprio come se fosse stato definito come const long count = 70000;. Inoltre, se si passa una di quelle versioni di countuna funzione in attesa int, qualsiasi compilatore sano le tratterà allo stesso modo.
Pete Becker,

1
Sono d'accordo con @PeteBecker - un costrutto come #define COUNT 70000non si tronca in un int, ma il compilatore lo considera come un tipo abbastanza grande da contenere quel numero. È vero che potrebbe non essere ovvio quando lo usi COUNTche non è un int, ma potresti comunque dire la stessa cosa di un const long.
Nick Gammon

2
"un #define sarà sempre un numero intero" Questo non è vero. Stai prendendo le regole dei letterali interi e le stai applicando alle macro del preprocessore. È come confrontare mele e musica pop. L'espressione COUNTnel tuo esempio viene sostituita prima della compilazione con l'espressione 70000, che ha un tipo definito dalle regole dei letterali, proprio come 2o 13Lo 4.0sono definite dalle regole dei letterali. Il fatto che usi #defineper alias quelle espressioni è irrilevante. Puoi usare #definealias frammenti arbitrari di codice C, se vuoi.
Corse di leggerezza con Monica il

2

Come principiante di 2 settimane ad Arduino, vorrei prendere in considerazione l'idea generale che Arduino fosse occupato da non programmatori. La maggior parte degli schizzi che ho esaminato, compresi quelli sul sito di Arduino, mostrano una totale mancanza di ordine, con schizzi che non funzionano e appena un commento coerente in vista. I diagrammi di flusso sono inesistenti e le "Librerie" sono un miscuglio non moderato.


0

La mia risposta è ... lo fanno perché funziona. Sto facendo fatica a non fare una domanda nella mia risposta del tipo "perché deve essere 'sbagliato'?"


3
Un tratto distintivo di un buon programmatore è che il codice riflette sempre le loro intenzioni.
Ignacio Vazquez-Abrams,

1
Stiamo ancora parlando di Arduinos, giusto? ;)
linhartr22

3
Arduino ha già una cattiva reputazione nella più grande comunità EE a causa dei progetti hardware mediocri-terribili messi in campo dalla comunità. Non dovremmo provare a dare una cazzata su qualcosa ?
Ignacio Vazquez-Abrams,

2
"La maggior parte dei progetti non comporta rischi di vita o di finanze ..." Nessuna sorpresa. Chi avrebbe voglia di coinvolgere Arduino dove c'è qualche possibilità di rischio dopo aver guardato la comunità in generale.
Ignacio Vazquez-Abrams,

2
È "sbagliato" non perché non funziona in una situazione particolare, ma perché, rispetto a farlo "bene", ci sono più situazioni in cui non funziona. Questo rende il codice fragile; le modifiche al codice possono causare misteriosi errori che richiedono tempo per il debug. Il controllo del tipo del compilatore e i messaggi di errore sono lì per aiutarti a rilevare questo tipo di errori prima, piuttosto che dopo.
Curt J. Sampson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.