Fråga:
Är det bättre att använda #define eller const int för konstanter?
Cybergibbons
2014-02-28 15:46:51 UTC
view on stackexchange narkive permalink

Arduino är en udda hybrid, där en del C ++ -funktioner används i den inbäddade världen - traditionellt en C-miljö. Faktum är att mycket Arduino-kod är väldigt C som.

C har traditionellt använt #define för konstanter. Det finns flera anledningar till detta:

  1. Du kan inte ställa in matrisstorlekar med const int .
  2. Du kan inte använda const int som etiketter för falluttalanden (även om detta fungerar i vissa kompilatorer)
  3. Du kan inte initialisera en const med en annan const kod>.

Du kan kontrollera den här frågan i StackOverflow för mer resonemang.

Så, vad ska vi använda för Arduino? Jag tenderar mot #define , men jag ser en del kod med const och en del med en blandning.

en bra optimerare gör att det blir mycket
Verkligen? Jag förstår inte hur en kompilator kommer att lösa saker som typsäkerhet, inte kan använda för att definiera matrislängd och så vidare.
Jag håller med. Dessutom, om du tittar på mitt svar nedan visar jag att det finns omständigheter när du inte riktigt vet vilken typ du ska använda, så '# definiera' är det självklara valet. Mitt exempel är att namnge analoga stift - som A5. Det finns ingen lämplig typ för den som kan användas som en "const" så det enda valet är att använda en "# define" och låta kompilatören ersätta den som textinmatning innan du tolkar innebörden.
Tre svar:
microtherion
2014-02-28 21:05:37 UTC
view on stackexchange narkive permalink

Det är viktigt att notera att const int inte beter sig identiskt i C och i C ++, så i själva verket flera av invändningarna mot det som har antydts i originalfrågan och i Peter Bloomfields omfattande svar är inte giltiga:

  • I C ++ är const int konstanter kompilera tidsvärden och kan användas för att ställa in arraygränser, som falletiketter etc.
  • const int konstanter upptar inte nödvändigtvis någon lagring. Om du inte tar deras adress eller förklarar dem externa, kommer de i allmänhet bara att ha en sammanställningstid.

Men för heltalskonstanter kan det ofta vara att föredra att använda en (namngiven eller anonym) enum . Jag gillar det här ofta för:

  • Det är bakåtkompatibelt med C.
  • Det är nästan lika typsäkert som const int (lika lite som typsäkert i C ++ 11).
  • Det ger ett naturligt sätt att gruppera relaterade konstanter.
  • Du kan till och med använda dem för en viss kontroll av namnområdet.

Så i ett idiomatiskt C ++ - program finns det ingen anledning att använda #definiera för att definiera en heltalskonstant. Även om du vill förbli C-kompatibel (på grund av tekniska krav, för att du börjar det gamla skolan, eller för att personer du arbetar med föredrar det på det sättet) kan du fortfarande använda enum och bör gör det istället för att använda #define .

Du tar upp några utmärkta poäng (speciellt om arraygränserna - jag hade inte insett standardkompilatorn med Arduino IDE som stödde det ännu). Det är inte riktigt korrekt att säga att en kompileringstidskonstant inte använder något lagringsutrymme, eftersom dess värde fortfarande måste förekomma i kod (dvs. programminne i stället för SRAM) var som helst det används. Det betyder att det påverkar tillgänglig Flash för alla typer som tar mer utrymme än en pekare.
"så i själva verket flera av invändningarna mot det som har antydts i den ursprungliga frågan" - varför är de inte giltiga i den ursprungliga frågan, eftersom det anges att detta är begränsningar för C?
@Cybergibbons Arduino är baserad på C ++, så det är inte klart för mig varför C endast begränsningar skulle vara relevanta (om inte din kod av någon anledning måste vara kompatibel med C också).
@PeterR.Bloomfield, min poäng om konstanter som inte kräver extra lagring begränsades till `const int`. För mer komplexa typer har du rätt i att lagring kan tilldelas, men ändå är det osannolikt att du har det sämre än med en # definiera.
Peter Bloomfield
2014-02-28 16:40:06 UTC
view on stackexchange narkive permalink

EDIT: microtherion ger ett utmärkt svar som korrigerar några av mina punkter här, särskilt om minnesanvändning.


Som du har identifierat finns det vissa situationer där du tvingas använda en #define , eftersom kompilatorn inte tillåter en const -variabel. På samma sätt tvingas du i vissa situationer använda variabler, till exempel när du behöver en rad värden (dvs. du kan inte ha en array med #define).

Det finns dock många andra situationer där det inte nödvändigtvis finns ett enda "rätt" svar. Här är några riktlinjer som jag skulle följa:

Typsäkerhet
Ur en allmän programmeringssynpunkt är const variabler vanligtvis att föredra (om möjligt). Den främsta anledningen till detta är typsäkerhet.

En #definiera (förprocessormakro) kopierar bokstavligen värdet direkt till varje plats i koden, vilket gör varje användning oberoende. Detta kan hypotetiskt leda till tvetydigheter, eftersom typen kan komma att lösas annorlunda beroende på hur / var den används.

En const -variabel är bara någonsin en typ, som bestäms genom sin förklaring och löstes under initialiseringen. Det kommer ofta att kräva en uttrycklig rollbesättning innan den kommer att bete sig annorlunda (även om det finns olika situationer där det säkert kan implicit typfrämjas). Åtminstone kan kompilatorn (om den är korrekt konfigurerad) avge en mer tillförlitlig varning när ett typproblem uppstår.

En möjlig lösning för detta är att inkludera en explicit cast eller ett typsuffix i en #definiera . Till exempel:

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

Det tillvägagångssättet kan dock i vissa fall orsaka syntaxproblem, beroende på hur den används.

Minnesanvändning
Till skillnad från allmänna datorer är minnet uppenbarligen till en premie när man arbetar med något som en Arduino. Att använda en const -variabel jämfört med en #define kan påverka var data lagras i minnet, vilket kan tvinga dig att använda den ena eller den andra.

  • const -variabler lagras (vanligtvis) i SRAM, tillsammans med alla andra variabler.
  • Bokstavsvärden som används i #define kommer ofta lagras i programutrymmet (Flash-minne), tillsammans med själva skissen.

(Observera att det finns olika saker som kan påverka exakt hur och var något lagras, till exempel kompilator konfiguration och optimering.)

SRAM och Flash har olika begränsningar (t.ex. 2 KB respektive 32 KB för Uno). För vissa applikationer är det ganska enkelt att ta slut på SRAM, så det kan vara till hjälp att flytta vissa saker till Flash. Det omvända är också möjligt, även om det troligen är mindre vanligt.

PROGMEM
Det är möjligt att få fördelarna med typsäkerhet samtidigt som man lagrar data i programutrymme (Flash) . Detta görs med nyckelordet PROGMEM . Det fungerar inte för alla typer, men det används vanligtvis för matriser med heltal eller strängar.

Det allmänna formuläret i dokumentationen är som följer:

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

Strängtabeller är lite mer komplicerade, men dokumentationen har fullständiga detaljer.

SDsolar
2018-03-26 22:58:08 UTC
view on stackexchange narkive permalink

För variabler av en viss typ som inte ändras under körning kan antingen vanligtvis användas.

För digitala pin-nummer som finns i variabler kan antingen fungera - som:

  const int ledPin = 13;  

Men det finns en omständighet där jag alltid använder #define

Det är att definiera analoga pin-nummer, eftersom de är alfanumeriska.

Visst, du kan svår- koda pin-numren som a2 , a3 etc. i hela programmet och kompilatorn vet vad de ska göra med dem. Om du sedan byter stift måste varje användning ändras.

Dessutom vill jag alltid ha mina stiftdefinitioner högst upp på ett ställe, så frågan blir vilken typ av const skulle vara lämpligt för en stift definierad som A5.

I dessa fall använder jag alltid #define

Spänningsdelare Exempel:

  //// read12 Läser spänning på 12V batteri //// SDsolar 8/8/18 // # define adcInput A5 // Spänningsdelarens utgång kommer in på Analog A5float R1 = 120000,0; // R1 för spänningsdelareingång från extern 0-15Vfloat R2 = 20000.0; // R2 för spänningsdelarens utgång till ADCfloat vRef = 4.8; // 9V på Vcc går genom regulatorfloat vTmp, vIn; int värde; .. void setup () {.// tillåter ADC att stabilisera värden = analogRead (adcPin); fördröjning (50); värde = analogRead (adcPin); fördröjning (50); värde = analogRead (adcPin); fördröjning (50); värde = analogRead (adcPin); fördröjning (50); värde = analogRead (adcPin); fördröjning (50); värde = analogRead (adcPin) ;. tomrumslinga () {.. värde = analogRead (adcPin); vTmp = värde * (vRef / 1024.0); vIn = vTmp / (R2 / (R1 + R2)); . .  

Alla installationsvariabler finns högst upp och det kommer aldrig att bli någon förändring av värdet på adcPin utom vid kompileringstid.

Inga bekymmer om vilken typ adcPin är. Och inget extra RAM-minne används i binären för att lagra en konstant.

Kompilatorn ersätter helt enkelt varje förekomst av adcPin med strängen A5 innan du kompilerar.


Det finns en intressant Arduino Forum tråd som diskuterar andra sätt att bestämma:

#define vs. const variabel (Arduino forum)

Utdrag:

Kodersättning:

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

Felsökningskod :

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

Definiera true och false som Boolean för att spara RAM

  I stället för att använda `const bool true = 1;` och samma för `false '# definiera true (boolean) 1 # define false (boolean) 0  

Mycket av det kommer till personliga preferenser, men det är tydligt att #define är mer mångsidig.

Under samma omständigheter kommer en "const" inte att använda mer RAM än en "# define". Och för de analoga stiften skulle jag definiera dem som "const uint8_t", även om "const int" inte skulle göra någon skillnad.
Du skrev "_a` const "använder faktiskt inte mer RAM [...] förrän det faktiskt används_". Du saknade min poäng: för det mesta använder en "const" inte RAM, även om den används. Sedan, "_ detta är en multipass kompilator_". Viktigast är att det är en _optimizing_ kompilator. När det är möjligt optimeras konstanter till [omedelbara operander] (https://en.wikipedia.org/wiki/Addressing_mode#Immediate/literal).


Denna fråga och svar översattes automatiskt från det engelska språket.Det ursprungliga innehållet finns tillgängligt på stackexchange, vilket vi tackar för cc by-sa 3.0-licensen som det distribueras under.
Loading...