Vraag:
Is het beter om #define of const int te gebruiken voor constanten?
Cybergibbons
2014-02-28 15:46:51 UTC
view on stackexchange narkive permalink

Arduino is een vreemde hybride, waar enige C ++ -functionaliteit wordt gebruikt in de embedded wereld - traditioneel een C-omgeving. Inderdaad, veel Arduino-code lijkt erg op C.

C gebruikt traditioneel #define s voor constanten. Hiervoor zijn een aantal redenen:

  1. U kunt geen array-grootten instellen met const int .
  2. U kunt const int als case statement labels (hoewel dit in sommige compilers wel werkt)
  3. Je kunt een const niet initialiseren met een andere const .

Je kunt deze vraag op StackOverflow aanvinken voor meer redenering.

Dus, wat moeten we gebruiken voor Arduino? Ik neig naar #define , maar ik zie dat sommige code const gebruikt en andere een blend.

een goede optimizer zal het betwistbaar maken
Werkelijk? Ik zie niet in hoe een compiler zaken als typeveiligheid gaat oplossen, de niet kan gebruiken om de array-lengte te definiëren, enzovoort.
Daar ben ik het mee eens. Bovendien, als je naar mijn antwoord hieronder kijkt, laat ik zien dat er omstandigheden zijn waarin je niet echt weet welk type je moet gebruiken, dus '# definieer' is de voor de hand liggende keuze. Mijn voorbeeld is het benoemen van analoge pinnen - zoals A5. Er is geen geschikt type voor dat kan worden gebruikt als een `const`, dus de enige keuze is om een` # define` te gebruiken en de compiler deze als tekstinvoer te laten vervangen voordat de betekenis wordt geïnterpreteerd.
Drie antwoorden:
#1
+23
microtherion
2014-02-28 21:05:37 UTC
view on stackexchange narkive permalink

Het is belangrijk op te merken dat const int zich niet identiek gedraagt ​​in C en in C ++, dus in feite zijn er verschillende bezwaren ertegen die in de originele vraag en in Peter Bloomfields 'uitgebreide antwoord zijn niet geldig:

  • In C ++ zijn const int constanten compileer tijdwaarden en kunnen worden gebruikt om array-limieten in te stellen, als case-labels, enz.
  • const int -constanten nemen niet noodzakelijk enige opslagruimte in beslag. Tenzij u hun adres aanneemt of ze extern aangeeft, zullen ze over het algemeen alleen een bestaan ​​hebben tijdens het compileren.

Voor constanten met gehele getallen kan het echter vaak de voorkeur hebben om een ​​(benoemde of anonieme) enum . Ik vind dit vaak leuk omdat:

  • Het is achterwaarts compatibel met C.
  • Het is bijna net zo type safe als const int (net zo min als type safe in C ++ 11).
  • Het biedt een natuurlijke manier om gerelateerde constanten te groeperen.
  • U kunt ze zelfs gebruiken voor een bepaalde mate van naamruimtecontrole.

Dus in een idiomatisch C ++ - programma is er geen enkele reden om #define te gebruiken om een ​​integerconstante te definiëren. Zelfs als je C-compatibel wilt blijven (vanwege technische vereisten, omdat je het old school kickt, of omdat mensen met wie je werkt het op die manier prefereren), kun je nog steeds enum gebruiken en zou doe dit in plaats van #define te gebruiken.

Je brengt een aantal uitstekende punten naar voren (vooral over de array-limieten - ik had me nog niet gerealiseerd dat de standaardcompiler met Arduino IDE dat ondersteunde). Het is echter niet helemaal correct om te zeggen dat een compilatietijdconstante geen opslagruimte gebruikt, omdat de waarde ervan nog steeds in code moet voorkomen (d.w.z. programmageheugen in plaats van SRAM) overal waar het wordt gebruikt. Dat betekent dat het van invloed is op beschikbare Flash voor elk type dat meer ruimte in beslag neemt dan een aanwijzer.
"dus in feite een aantal van de bezwaren ertegen waarop in de oorspronkelijke vraag is gezinspeeld" - waarom zijn ze niet geldig in de oorspronkelijke vraag, aangezien wordt gesteld dat dit beperkingen zijn van C?
@Cybergibbons Arduino is gebaseerd op C ++, dus het is mij niet duidelijk waarom alleen beperkingen van C relevant zouden zijn (tenzij je code om de een of andere reden ook compatibel moet zijn met C).
@PeterR.Bloomfield, mijn punt over constanten die geen extra opslag vereisen, was beperkt tot `const int`. Voor complexere typen heb je gelijk dat opslagruimte kan worden toegewezen, maar toch is het onwaarschijnlijk dat het slechter af is dan met een `# define`.
#2
+7
Peter Bloomfield
2014-02-28 16:40:06 UTC
view on stackexchange narkive permalink

EDIT: microtherion geeft een uitstekend antwoord dat enkele van mijn punten hier corrigeert, met name over geheugengebruik.


Zoals je hebt vastgesteld, zijn er bepaalde situaties waar je gedwongen wordt een #define te gebruiken, omdat de compiler een const -variabele niet toestaat. Evenzo wordt u in sommige situaties gedwongen variabelen te gebruiken, bijvoorbeeld wanneer u een reeks waarden nodig heeft (u kunt dus geen reeks #define) hebben.

Er zijn echter veel andere situaties waarin er niet per se één 'juist' antwoord is. Hier zijn enkele richtlijnen die ik zou volgen:

Typeveiligheid
Vanuit een algemeen programmeerstandpunt hebben const -variabelen meestal de voorkeur (waar mogelijk). De belangrijkste reden hiervoor is type-veiligheid.

Een #define (preprocessormacro) kopieert de letterlijke waarde direct naar elke locatie in code, waardoor elk gebruik onafhankelijk wordt. Dit kan hypothetisch leiden tot dubbelzinnigheden, omdat het type uiteindelijk anders kan worden opgelost afhankelijk van hoe / waar het wordt gebruikt.

Een const -variabele is altijd maar één type, dat wordt bepaald door zijn verklaring, en opgelost tijdens initialisatie. Het vereist vaak een expliciete cast voordat het zich anders zal gedragen (hoewel er verschillende situaties zijn waarin het veilig impliciet kan worden gepromoot). Op zijn minst kan de compiler (indien correct geconfigureerd) een betrouwbaardere waarschuwing geven wanneer een typeprobleem optreedt.

Een mogelijke oplossing hiervoor is om een ​​expliciete cast of een type-achtervoegsel op te nemen in een #define . Bijvoorbeeld:

  #define THE_ANSWER (int8_t) 42 # define NOT_QUITE_PI 3.14f  

Die benadering kan in sommige gevallen echter syntaxisproblemen veroorzaken, afhankelijk van hoe het wordt gebruikt.

Geheugengebruik
In tegenstelling tot computergebruik voor algemene doeleinden, is geheugen duidelijk duur als het gaat om zoiets als een Arduino. Het gebruik van een const -variabele versus een #define kan van invloed zijn op waar de gegevens in het geheugen worden opgeslagen, waardoor u mogelijk de ene of de andere moet gebruiken.

  • const -variabelen worden (meestal) opgeslagen in SRAM, samen met alle andere variabelen.
  • Letterlijke waarden die worden gebruikt in #define zullen vaak worden opgeslagen in programmaruimte (Flash-geheugen), naast de schets zelf.

(Merk op dat er verschillende dingen zijn die precies kunnen beïnvloeden hoe en waar iets wordt opgeslagen, zoals compiler configuratie en optimalisatie.)

SRAM en Flash hebben verschillende beperkingen (bijvoorbeeld respectievelijk 2 KB en 32 KB voor de Uno). Voor sommige toepassingen is het vrij eenvoudig om zonder SRAM te komen, dus het kan handig zijn om sommige dingen naar Flash te verplaatsen. Het omgekeerde is ook mogelijk, hoewel waarschijnlijk minder gebruikelijk.

PROGMEM
Het is mogelijk om de voordelen van typeveiligheid te krijgen terwijl de gegevens ook in programmaruimte (Flash) worden opgeslagen. . Dit wordt gedaan met het trefwoord PROGMEM . Het werkt niet voor alle typen, maar wordt vaak gebruikt voor arrays van gehele getallen of strings.

De algemene vorm die wordt gegeven in de documentatie is als volgt:

  dataType variableName [] PROGMEM = {dataInt0, dataInt1, dataInt3 ...}; 

String-tabellen zijn iets gecompliceerder, maar de documentatie bevat alle details.

#3
+1
SDsolar
2018-03-26 22:58:08 UTC
view on stackexchange narkive permalink

Voor variabelen van een gespecificeerd type die niet worden gewijzigd tijdens de uitvoering, kunnen beide meestal worden gebruikt.

Voor digitale pincodes die zijn opgenomen in variabelen, kunnen beide werken - zoals:

  const int ledPin = 13;  

Maar er is één omstandigheid waarin ik altijd gebruik #define

Het is om analoge pincodes te definiëren, aangezien ze alfanumeriek zijn.

Natuurlijk kun je moeilijk- codeer de pincodes als a2 , a3 , etc. door het hele programma heen en de compiler weet wat hij ermee moet doen. Als je vervolgens van pinnen verandert, moet elk gebruik worden gewijzigd.

Bovendien wil ik altijd dat mijn pindefinities bovenaan allemaal op één plek staan, dus de vraag wordt welk type const zou geschikt zijn voor een pin gedefinieerd als A5.

In die gevallen gebruik ik altijd #define

Voorbeeld spanningsdeler:

  //// read12 Leest spanning van 12V-batterij //// SDsolar 8/8/18 // # define adcInput A5 // De uitgang van de spanningsdeler komt binnen op Analog A5float R1 = 120000,0; // R1 voor spanningsdeler input van externe 0-15Vfloat R2 = 20000.0; // R2 voor uitgang van de spanningsdeler naar ADCfloat vRef = 4.8; // 9V op Vcc gaat door de regulatorfloat vTmp, vIn; int waarde; .. void setup () {.// laat ADC toe om de waarde te stabiliseren = analogRead (adcPin); vertraging (50); waarde = analogRead (adcPin); vertraging (50); waarde = analogRead (adcPin); vertraging (50); waarde = analogRead (adcPin); vertraging (50); waarde = analogRead (adcPin); vertraging (50); waarde = analogRead (adcPin) ;. void loop () {.. value = analogRead (adcPin); vTmp = waarde * (vRef / 1024.0); vIn = vTmp / (R2 / (R1 + R2)); . .  

Alle instellingsvariabelen staan ​​helemaal bovenaan en er zal nooit een wijziging plaatsvinden in de waarde van adcPin behalve tijdens het compileren.

Geen zorgen over wat voor type adcPin is. En er wordt geen extra RAM gebruikt in het binaire bestand om een ​​constante op te slaan.

De compiler vervangt eenvoudig elke instantie van adcPin door de tekenreeks A5 alvorens te compileren.


Er is een interessante Arduino Forum-thread die andere manieren bespreekt om te beslissen:

#define vs. const variabele (Arduino-forum)

Excertps:

Codevervanging:

  #define FOREVER voor (;;) FOREVER {if (serial.available () > 0) ...}  

Foutopsporingscode :

  #ifdef DEBUG #define DEBUG_PRINT (x) Serial.println (x) #else #define DEBUG_PRINT (x) #endif  

true en false als Boolean om RAM te besparen

  In plaats van `const bool true = 1;` en hetzelfde voor `false` # definieer true (boolean) 1 # define false (boolean) 0  

Veel komt neer op persoonlijke voorkeur, maar het is duidelijk dat #define is veelzijdiger.

In dezelfde omstandigheden zal een `const` niet meer RAM gebruiken dan een` # define`. En voor de analoge pinnen zou ik ze definiëren als `const uint8_t`, hoewel` const int` geen verschil zou maken.
Je schreef "_a` const "gebruikt eigenlijk niet meer RAM [...] totdat het daadwerkelijk wordt gebruikt_". Je hebt mijn punt gemist: meestal gebruikt een `const` geen RAM, zelfs als het wordt gebruikt_. Vervolgens "_this is a multipass compiler_". Het belangrijkste is dat het een _optimizing_ compiler is. Waar mogelijk worden constanten geoptimaliseerd in [directe operanden] (https://en.wikipedia.org/wiki/Addressing_mode#Immediate/literal).


Deze Q&A is automatisch vertaald vanuit de Engelse taal.De originele inhoud is beschikbaar op stackexchange, waarvoor we bedanken voor de cc by-sa 3.0-licentie waaronder het wordt gedistribueerd.
Loading...