Warum eine int-Variable für einen Pin verwenden, wenn const int, enum oder #define viel sinnvoller sind?

24

Warum verwenden Benutzer eine Variable, um eine PIN-Nummer anzugeben, wenn sich die PIN während der Ausführung des Codes wahrscheinlich nicht ändert?

Oft sehe ich ein intWesen, das für eine Stiftdefinition verwendet wird,

int led = 13;

wenn die Verwendung von a const int

const int led = 13;

oder enum, oder#define

#define LED 13

macht viel mehr Sinn.

Es ist sogar in Tutorials auf der Arduino-Site, zum Beispiel das erste Tutorial, das die meisten Leute ausführen, Blink .

Ich lese irgendwo , const intwas vorgezogen wird #define. Warum wird dies nicht von Anfang an ermutigt, anstatt den Menschen von Anfang an zu erlauben, schlechte Gewohnheiten zu entwickeln? Ich habe es vor einiger Zeit bemerkt, aber in letzter Zeit hat es mich gereizt, daher die Frage.

Speicher / Verarbeitung / Rechen weise ein const int, enumoder was das betrifft #define, besser als eine einfache int, nimmt also weniger Speicher, gespeichert in verschiedenen Speicher (Flash, EEPROM, SRAM), eine schnellere Ausführung, schneller zu kompilieren?


Dies scheint ein Duplikat von zu sein. Ist es besser, #define oder const int für Konstanten zu verwenden? Aber ich gehe auf die Frage ein, warum Menschen Variablen verwenden und wie sich die Leistung verbessert, wenn sie dies nicht tun, anstatt welche Art von Konstante besser ist.

Greenonline
quelle
9
Weil schrecklich schrecklich erzeugt. Die meisten Hobbyisten sind keine erfahrenen Programmierer und lehren andere Hobbyisten schlechte Gewohnheiten.
Ignacio Vazquez-Abrams
1
Mit Stiften insbesondere die vereinfachte Form der grundlegenden Arduino - API - Funktionen wie digital fördert nicht die richtigen Embedded - Design, dh unter Verwendung von Masken und eine einzige Speicheradresse für die gesamten Hafen
crasic

Antworten:

20
const int led = 13;

Das ist die richtige Methode. Oder auch:

const byte led = 13;

Wie viele Pins hast du?

Einige der Tutorials haben nicht so viele Qualitätskontrollen durchlaufen, wie sie haben könnten.

Die Leistung wird bei der Verwendung besser const byte, verglichen mit intdem Compiler, der möglicherweise klug genug ist, um zu erkennen, was Sie tun.

Was Sie tun können, ist, Menschen sanft zu ermutigen, effizientere Techniken zu verwenden, indem Sie sie in Ihrem eigenen Code verwenden.


Antworten auf Kommentare

  1. Ein Kommentator hat vorgeschlagen, dass dies bytenicht Standard C ist. Dies ist korrekt, es handelt sich jedoch um eine Arduino StackExchange-Site, und ich glaube, dass die Verwendung von Standardtypen, die von der Arduino IDE bereitgestellt werden, akzeptabel ist.

    In Arduino.h gibt es diese Zeile:

    typedef uint8_t byte;

    Beachten Sie, dass dies nicht genau dasselbe ist wie unsigned char. Siehe uint8_t vs unsigned char und Wann ist uint8_t ≠ unsigned char? .

  2. Ein anderer Kommentator hat vorgeschlagen, dass die Verwendung von Byte nicht unbedingt die Leistung verbessert, da Zahlen, die kleiner sind als intdie, auf die intheraufgestuft wird (siehe Regeln für die Heraufstufung von Ganzzahlen, wenn Sie mehr dazu möchten).

    Im Kontext eines konstanten Bezeichners generiert der Compiler jedoch in jedem Fall effizienten Code. Das Zerlegen von "blink" ergibt beispielsweise Folgendes in der ursprünglichen Form:

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

    Tatsächlich wird derselbe Code generiert, unabhängig davon, ob 13:

    • Ist ein wörtliches
    • Ist ein #define
    • Ist ein const int
    • Ist ein const byte

Der Compiler weiß, wann eine Zahl in ein Register passt und wann nicht. Es wird jedoch empfohlen , eine Codierung zu verwenden, die auf Ihre Absicht hinweist . Machen Sie constes deutlich, dass sich die Zahl nicht ändert, und machen Sie es byte(oderuint8_t ) klar machen, dass Sie eine kleine Zahl erwarten.


Verwirrende Fehlermeldungen

Ein weiterer wichtiger Grund, den Sie vermeiden sollten, #definesind die Fehlermeldungen, die Sie erhalten, wenn Sie einen Fehler machen. Betrachten Sie diese "Blink" -Skizze, die einen Fehler aufweist:

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

Auf der Oberfläche sieht es OK aus, aber es erzeugt diese Fehlermeldungen:

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

Sie schauen auf die erste hervorgehobene Zeile (Zeile 4) und sehen nicht einmal ein "=" - Symbol. Außerdem sieht die Leitung gut aus. Jetzt ist es ziemlich offensichtlich, wo das Problem liegt ( = 13wird ersetzt)LED ), aber wenn die Zeile 400 Zeilen weiter unten im Code steht, liegt das Problem nicht an der Art und Weise, wie die LED definiert ist.

Ich habe schon viele Male gesehen, wie Menschen darauf hereinfielen (einschließlich meiner selbst).

Nick Gammon
quelle
Wie viele Pins hast du? Nick ist ein sehr guter Punkt, da die meisten Boards nur im Bereich der Zehner, nicht der Hunderter (dh größer als 255) liegen, also ein intOverkill ist ... das heißt, bis Arduino endlich mit dem Tera- Board herauskommt ... :-)
Greenonline
2
C hat keinen byteTyp . Du meinst unsigned char.
Kevin
Die Leistung wird nicht unbedingt besser mit bytestatt int, da in den meisten Kontexten Integer - Wert mit Typ kleiner als intgefördert zu int.
Pete Becker
1
C doesn't have a byte type. You mean unsigned char.- Meine Antwort war im Arduino-Kontext, der dies hat typedef uint8_t byte;. Für einen Arduino ist die Verwendung bytealso in Ordnung.
Nick Gammon
Performance won't necessarily be better with byte instead of int- siehe geänderten Beitrag.
Nick Gammon
19

Wie Ignacio zu Recht feststellt, liegt es im Grunde daran, dass sie es nicht besser wissen. Und sie wissen es nicht besser, weil die Leute, die sie unterrichteten (oder die Ressourcen, die sie beim Lernen verwendeten), es nicht besser wussten.

Ein Großteil des Arduino-Codes und der Tutorials wurde von Personen geschrieben, die noch nie eine Programmierschulung absolviert haben, und werden von Personen, die selbst eine Autodidaktin sind und keine ausreichende Programmierschulung besitzen, aus Ressourcen "autodidaktisch" unterrichtet.

Viele der Code-Schnipsel von Tutorials, die ich überall sehe (und insbesondere die, die nur in YouTube-Videos verfügbar sind - urgh), wären eine nicht bestandene Marke, wenn ich sie in einer Prüfung markieren würde.

Ja, a constwird einem nicht konstanten vorgezogen, und sogar einem #define, weil:

  • A const(wie ein #define, im Gegensatz zu einem Nicht-Konstanten) reserviert keinen RAM
  • A const(wie eine Nicht-Konstante, aber anders als a #define) gibt dem Wert einen expliziten Typ

Der zweite Punkt ist dort von besonderem Interesse. Sofern bei eingebettetem Typ-Casting ( (long)3) oder einem Typ-Suffix ( 3L) oder dem Vorhandensein eines Dezimalpunkts ( 3.0) nicht ausdrücklich anders angegeben , ist #defineeine Zahl immer eine Ganzzahl, und alle mit diesem Wert durchgeführten mathematischen Berechnungen sind wie eine ganze Zahl. Meistens ist das kein Problem, aber Sie können in interessante Szenarien geraten, wenn Sie versuchen, #defineeinen Wert zu speichern, der größer als eine Ganzzahl ist, #define COUNT 70000und dann eine mathematische Operation mit anderen intWerten ausführen . Mit a können constSie dem Compiler mitteilen, dass "dieser Wert als dieser Variablentyp behandelt werden soll". Sie würden also stattdessen Folgendes verwenden: const long count = 70000;und alles würde wie erwartet funktionieren.

Es hat auch den Anstoßeffekt, dass es den Typ überprüft, wenn der Wert um die Stelle herum übergeben wird. Versuchen Sie, eine const longan eine Funktion zu übergeben, die eine erwartet, intund die sich über die Einschränkung des Variablenbereichs beschwert (oder die je nach Szenario gar nicht kompiliert werden kann). Tun Sie das mit a #defineund es würde Ihnen nur still weiter die falschen Ergebnisse liefern und Sie stundenlang am Kopf kratzen lassen.

Majenko
quelle
7
Es ist zu beachten, dass eine constVariable je nach Kontext möglicherweise RAM benötigt, z. B. wenn sie mit dem Rückgabewert einer Nicht-Constexpr-Funktion initialisiert wird.
Peter Bloomfield
Ebenso muss const int foo = 13; bar(&foo);der Compiler auf jeden Fall den tatsächlichen Speicher zuweisen foo.
Ilmari Karonen
3
Wenn Sie ein Makro definieren, das auf einen Wert erweitert wird, der nicht in einen intCompiler passt, wird der Wert als der kleinste Typ behandelt, in den er passt (Modulo-Regeln für vorzeichenbehaftete und nicht vorzeichenbehaftete). Wenn Sie auf einem System sind , wo int16 Bit ist, #define count 70000wird in Folge count, die wie ein long, als ob es so definiert worden war const long count = 70000;. Wenn Sie eine dieser Versionen countan eine erwartete Funktion übergeben int, behandelt sie jeder vernünftige Compiler gleich.
Pete Becker
1
Ich stimme @PeteBecker zu - ein Konstrukt wie #define COUNT 70000wird nicht in ein Int abgeschnitten, aber der Compiler behandelt es als einen Typ, der groß genug ist, um diese Zahl aufzunehmen. Es ist wahr, dass es vielleicht nicht offensichtlich ist, wenn Sie verwenden, COUNTdass es kein Int ist, aber Sie könnten das Gleiche über ein Int sagen const long.
Nick Gammon
2
"ein #define wird immer eine ganze Zahl sein" Das ist nicht wahr. Sie nehmen die Regeln von Integer-Literalen und wenden sie auf Präprozessor-Makros an. Es ist wie ein Vergleich von Äpfeln und Popmusik. Der Ausdruck COUNTin Ihrem Beispiel wird vor der Kompilierung durch den Ausdruck ersetzt 70000, dessen Typ durch die Regeln der Literale definiert ist, genau wie 2oder 13Loder 4.0werden durch die Regeln der Literale definiert. Die Tatsache, dass Sie #definediese Ausdrücke als Alias ​​verwenden, ist irrelevant. Sie können #definebeliebige Teile von C-Code als Alias ​​verwenden, wenn Sie möchten.
Leichtigkeit Rennen mit Monica
2

Als zweiwöchiger Neuling bei Arduino würde ich die allgemeine Idee aufgreifen, dass Arduino von Nicht-Programmierern besetzt ist. Die meisten von mir untersuchten Skizzen, einschließlich derjenigen auf der Arduino-Website, weisen einen völligen Mangel an Ordnung auf. Skizzen, die nicht funktionieren, und kaum ein zusammenhängender Kommentar sind in Sicht. Flussdiagramme sind nicht vorhanden, und die "Bibliotheken" sind ein nicht moderiertes Durcheinander.

Jim
quelle
0

Meine Antwort ist ... sie machen es, weil es funktioniert. Es fällt mir schwer, in meiner Antwort keine Frage zu stellen wie "Warum muss es 'falsch' sein?"

linhartr22
quelle
3
Ein Kennzeichen eines guten Programmierers ist, dass der Code immer ihre Absichten widerspiegelt.
Ignacio Vazquez-Abrams
1
Wir reden immer noch über Arduinos, oder? ;)
linhartr22
3
Arduino hat bereits einen schlechten Ruf in der größeren EE-Community aufgrund der mittelmäßigen bis schrecklichen Hardware-Designs, die von der Community veröffentlicht wurden. Sollten wir nicht versuchen, etwas auszutricksen ?
Ignacio Vazquez-Abrams
2
"Die meisten Projekte bergen kein Risiko für Leben oder Finanzen ..." Keine Überraschung. Wer würde Arduino einbeziehen wollen , wenn es nach einem Blick auf die Community im Allgemeinen eine Chance auf Risiken gibt?
Ignacio Vazquez-Abrams
2
Es ist „falsch“, nicht weil es in einer bestimmten Situation nicht funktioniert, sondern weil es, verglichen mit „richtig“, mehr Situationen gibt, in denen es nicht funktioniert. Dies macht den Code zerbrechlich. Änderungen am Code können zu mysteriösen Fehlern führen, die die Debugging-Zeit in Anspruch nehmen. Die Typprüfung und Fehlermeldungen des Compilers sollen Ihnen helfen, solche Fehler eher früher als später zu erkennen.
Curt J. Sampson