Pourquoi utiliser une variable int pour une broche lorsque const int, enum ou #define est beaucoup plus logique

24

Pourquoi les gens utilisent-ils une variable pour spécifier un numéro de code PIN lorsque le code PIN est peu susceptible de changer tout au long de l'exécution du code?

Plusieurs fois, je vois un intêtre utilisé pour une définition de broche,

int led = 13;

lorsque l'utilisation d'un const int

const int led = 13;

ou enum, ou#define

#define LED 13

est beaucoup plus logique.

C'est même dans les tutoriels sur le site Arduino, par exemple, le premier tutoriel que la plupart des gens exécutent, Blink .

J'ai lu quelque part qui const intest préféré à #define. Pourquoi cela n'est-il pas encouragé dès le début, plutôt que de permettre aux gens de développer de mauvaises habitudes, dès le départ? Je l'ai remarqué il y a quelque temps, mais récemment, il a commencé à m'énerver, d'où la question.

En ce qui concerne la mémoire / le traitement / l'informatique, est-ce que const int, enumou d'ailleurs #define, mieux qu'une simple int, c'est-à-dire occupe moins de mémoire, stockée dans une mémoire différente (Flash, EEPROM, SRAM), exécution plus rapide, compilation plus rapide?


Cela peut sembler être un doublon de Est-il préférable d'utiliser #define ou const int pour les constantes? , mais j'aborde la question de savoir pourquoi les gens utilisent des variables et comment les performances s'améliorent quand elles ne le font pas, plutôt que quel type de constante est meilleur.

Greenonline
la source
9
Parce que terrible engendre terrible. La plupart des amateurs ne sont pas des programmeurs chevronnés et enseignent donc aux autres amateurs de mauvaises habitudes.
Ignacio Vazquez-Abrams
1
Avec les broches en particulier, la forme simpliste des fonctions de base de l'API Arduino comme digitalWrite n'encourage pas une conception intégrée appropriée, c'est-à-dire en utilisant des masques et une seule adresse mémoire pour l'ensemble du port
crasic

Réponses:

20
const int led = 13;

C'est la bonne méthode. Ou même:

const byte led = 13;

Combien de broches avez-vous?

Certains didacticiels n'ont pas tout à fait subi autant de contrôle de la qualité qu'ils auraient pu.

Les performances seront meilleures à utiliser const byte, comparez à, intmais le compilateur peut être suffisamment intelligent pour réaliser ce que vous faites.

Ce que vous pouvez faire est d'encourager doucement les gens à utiliser des techniques plus efficaces en les utilisant dans votre propre code.


Réponses aux commentaires

  1. Un commentateur a suggéré que ce byten'est pas la norme C. C'est correct, mais il s'agit d'un site Arduino StackExchange, et je pense que l'utilisation de types standard fournis par l'IDE Arduino est acceptable.

    Dans Arduino.h, il y a cette ligne:

    typedef uint8_t byte;

    Notez que ce n'est pas exactement le même que unsigned char. Voir uint8_t vs caractère non signé et quand uint8_t char caractère non signé? .

  2. Un autre intervenant a suggéré que l'utilisation d'octets n'améliorera pas nécessairement les performances, car des nombres plus petits que ceux intqui seront promus int(voir Règles de promotion d'entiers si vous en voulez plus).

    Cependant, dans le contexte d'un identificateur const , le compilateur générera un code efficace dans tous les cas. Par exemple, le démontage du "clignotement" donne ceci sous la forme originale:

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

    En fait, il génère le même code que 13:

    • Est un littéral
    • Est un #define
    • Est un const int
    • Est un const byte

Le compilateur sait quand il peut rentrer un nombre dans un registre et quand il ne le peut pas. Cependant, il est recommandé d'utiliser un codage qui indique votre intention . Le faire constindique clairement que le nombre ne changera pas et le fait byte(ou uint8_t) indique clairement que vous attendez un petit nombre.


Messages d'erreur déroutants

Une autre raison majeure à éviter #defineest les messages d'erreur que vous obtenez si vous faites une erreur. Considérez ce croquis "clignotant" qui a une erreur:

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

En surface, il semble OK, mais il génère ces messages d'erreur:

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

Vous regardez la première ligne en surbrillance (ligne 4) et ne voyez même pas de symbole "=". De plus, la ligne semble bien. Maintenant, il est assez évident de savoir quel est le problème ici ( = 13est remplacé LED), mais lorsque la ligne se trouve 400 lignes plus loin dans le code, il n'est pas évident que le problème réside dans la façon dont la LED est définie.

J'ai vu des gens tomber à plusieurs reprises (y compris moi-même).

Nick Gammon
la source
Combien de broches avez-vous? est un très bon point Nick, car la plupart des planches n'ont que des dizaines, pas des centaines (c'est-à-dire supérieures à 255), donc intc'est exagéré ... c'est-à-dire jusqu'à ce qu'Arduino finisse par sortir avec la carte Tera ... :-)
Greenonline
2
C n'a pas de bytetype . Tu veux dire unsigned char.
Kevin
Les performances ne seront pas nécessairement meilleures avec byteau lieu de int, car dans la plupart des contextes, une valeur entière avec des types plus petits que ceux intpromus int.
Pete Becker
1
C doesn't have a byte type. You mean unsigned char.- Ma réponse était dans le contexte Arduino, qui a cela typedef uint8_t byte;. Donc, pour un Arduino, l'utilisation byteest OK.
Nick Gammon
Performance won't necessarily be better with byte instead of int- voir poste modifié.
Nick Gammon
19

Comme le dit à juste titre Ignacio, c'est essentiellement parce qu'ils ne savent pas mieux. Et ils ne savent pas mieux parce que les gens qui les ont enseignés (ou les ressources qu'ils ont utilisées pour apprendre) ne savaient pas mieux.

Une grande partie du code Arduino et des didacticiels sont rédigés par des personnes qui n'ont jamais reçu de formation en programmation et sont très "autodidactes" à partir de ressources par des personnes qui sont elles-mêmes très autodidactes sans formation appropriée en programmation.

De nombreux extraits de code de didacticiel que je vois autour de moi (et en particulier ceux qui ne sont disponibles que dans les vidéos YouTube --- urgh) seraient une note d'échec si je les notais lors d'un examen.

Oui, a constest préféré à un non-const, et même à a #define, car:

  • A const(comme un #define, contrairement à un non-const) n'alloue pas de RAM
  • A const(comme un non-const, mais contrairement à a #define) donne à la valeur un type explicite

Le deuxième point est particulièrement intéressant. Sauf indication contraire avec le transtypage de type intégré ( (long)3) ou un suffixe de type ( 3L) ou la présence d'un point décimal ( 3.0), #defineun nombre sera toujours un entier et toutes les mathématiques effectuées sur cette valeur seront comme s'il s'agissait d'un entier. La plupart du temps, ce n'est pas un problème, mais vous pouvez rencontrer des scénarios intéressants lorsque vous essayez #defineune valeur plus grande qu'un entier peut stocker, comme #define COUNT 70000et ensuite effectuer une opération mathématique avec d'autres intvaleurs. En utilisant un, constvous pouvez dire au compilateur "Cette valeur doit être traitée comme ce type de variable" - vous utiliseriez donc à la place: const long count = 70000;et tout fonctionnerait comme prévu.

Il a également pour effet d'activer le contrôle du type lors du passage de la valeur autour de l'endroit. Essayez de passer un const longà une fonction qui attend un intet il se plaindrait de réduire la plage de variables (ou même de ne pas compiler complètement selon le scénario). Faites cela avec un #defineet cela continuerait silencieusement à vous donner les mauvais résultats et vous laisserait vous gratter la tête pendant des heures.

Majenko
la source
7
Il convient de noter qu'une constvariable peut nécessiter de la RAM, selon le contexte, par exemple si elle est initialisée à l'aide de la valeur de retour d'une fonction non constexpr.
Peter Bloomfield
De même, il const int foo = 13; bar(&foo);faudra certainement que le compilateur alloue la mémoire réelle pour foo.
Ilmari Karonen
3
Si vous définissez une macro qui se développe en une valeur qui ne rentre pas dans un, intle compilateur traite la valeur comme ayant le plus petit type dans lequel elle rentrera (règles modulo sur signé vs non signé). Si vous êtes sur un système avec int16 bits, #define count 70000cela countressemblera à un long, comme s'il avait été défini comme const long count = 70000;. De plus, si vous passez l'une de ces versions de countà une fonction attendue int, tout compilateur sensé les traitera de la même manière.
Pete Becker
1
Je suis d'accord avec @PeteBecker - une construction comme celle- #define COUNT 70000ci ne tronque pas en entier, mais le compilateur la traite comme un type suffisamment grand pour contenir ce nombre. Il est vrai que ce n'est pas évident quand vous utilisez COUNTque ce n'est pas un int, mais vous pouvez const longquand même dire la même chose à propos d'un .
Nick Gammon
2
"un #define sera toujours un entier" Ce n'est pas vrai. Vous prenez les règles des littéraux entiers et les appliquez aux macros de préprocesseur. C'est comme comparer des pommes et de la musique pop. L'expression COUNTde votre exemple est remplacée avant la compilation par l'expression 70000, dont le type est défini par les règles des littéraux, tout comme 2ou 13Lou 4.0sont définis par les règles des littéraux. Le fait que vous utilisez #definepour alias ces expressions est sans importance. Vous pouvez utiliser #definepour alias des morceaux arbitraires de code C, si vous le souhaitez.
Courses de légèreté avec Monica
2

En tant que débutant de 2 semaines pour Arduino, je reprendrais l'idée générale qu'Arduino est occupé par des non-programmeurs. La plupart des croquis que j'ai examinés, y compris ceux du site Arduino, montrent un manque total d'ordre, avec des croquis qui ne fonctionnent pas et à peine un commentaire cohérent en vue. Les organigrammes sont inexistants et les "bibliothèques" sont un fouillis non modéré.

Jim
la source
0

Ma réponse est ... ils le font parce que ça marche. J'ai du mal à ne pas poser de question dans ma réponse telle que "pourquoi faut-il que ce soit" faux "?"

linhartr22
la source
3
Une caractéristique d'un bon programmeur est que le code reflète toujours ses intentions.
Ignacio Vazquez-Abrams
1
Nous parlons toujours d'Arduinos, non? ;)
linhartr22
3
Arduino a déjà un mauvais représentant dans la grande communauté EE en raison des conceptions matérielles médiocres à terribles proposées par la communauté. Ne devrions-nous pas essayer de donner une merde à quelque chose ?
Ignacio Vazquez-Abrams
2
"La plupart des projets ne comportent pas de risques pour la vie ou les finances ..." Pas de surprise là-bas. Qui veulent impliquer Arduino où il y a une chance de risque après avoir regardé la communauté en général.
Ignacio Vazquez-Abrams
2
C'est «faux» non pas parce que cela ne fonctionne pas dans une situation particulière, mais parce que, par rapport à ce qu'il est «bien», il y a plus de situations dans lesquelles cela ne fonctionne pas . Cela rend le code fragile; les modifications apportées au code peuvent provoquer de mystérieux échecs qui réduisent le temps de débogage. La vérification du type du compilateur et les messages d'erreur sont là pour vous aider à détecter ce type d'erreurs plus tôt que tard.
Curt J. Sampson