Küsimus:
Kas konstantide jaoks on parem kasutada #define või const int?
Cybergibbons
2014-02-28 15:46:51 UTC
view on stackexchange narkive permalink

Arduino on paaritu hübriid, kus manustatud maailmas kasutatakse mõnda C ++ funktsionaalsust - traditsiooniliselt C-keskkonda. Tõepoolest, paljud Arduino koodid on siiski väga C-laadsed.

C on konstantide jaoks traditsiooniliselt kasutanud #define s. Sellel on mitu põhjust:

  1. Massiivi suurusi ei saa määrata, kasutades const int .
  2. Te ei saa kasutada const int juhtumilausete siltidena (ehkki see töötab mõnes kompilaatoris)
  3. const ei saa initsialiseerida teise const kood>.

Lisateabe saamiseks vaadake StackOverflow'is seda küsimust.

Mida peaksime siis Arduino jaoks kasutama? Ma kaldun #define poole, kuid näen, et mõni kood kasutab const ja mõni segu.

hea optimeerija muudab selle vaieldavaks
Kas tõesti? Ma ei näe, kuidas kompilaator hakkab lahendama selliseid asju nagu tüübi ohutus, kui ta ei saa massiivi pikkuse ja muu määramiseks kasutada.
Ma nõustun. Lisaks, kui vaatate minu allolevat vastust, näitan, et on olukordi, kus te ei tea tegelikult, millist tüüpi kasutada, nii et ilmne valik on "# define". Minu näide on analoognõelte nimetamisel - nagu A5. Selle jaoks pole ühtegi sobivat tüüpi, mida saaks kasutada `const'ina, seega on ainus valik kasutada märget # # define ja lasta kompilaatoril see enne teksti tähenduse tõlgendamist tekstisisestusena asendada.
Kolm vastused:
#1
+23
microtherion
2014-02-28 21:05:37 UTC
view on stackexchange narkive permalink

Oluline on märkida, et const int ei käitu C ja C ++ keeles identselt, nii et tegelikult on mitu selle vastu esitatud vastuväidet, millele on viidatud algne küsimus ja Peter Bloomfieldsi ulatuslik vastus ei kehti:

  • C ++ keeles kompileerivad const int konstandid ajaväärtusi ja neid saab kasutada massiivipiirangute määramiseks, nagu juhtumite sildid jne.
  • const int konstandid ei pruugi tingimata ühtegi mäluruumi hõivata. Kui te ei võta nende aadressi ega kuuluta neid väliseks, on neil tavaliselt ainult kompileeritud aja olemasolu.

Kuid täisarvukonstandide puhul võib sageli olla eelistatav kasutada (nimega või anonüümset) enum . Mulle meeldib see sageli, sest:

  • see on tahapoole ühilduv C-ga.
  • see on peaaegu sama tüüpi turvaline kui const int (iga bitti sama tüüpi kui seif C ++ 11).
  • See pakub loomulikku viisi seotud konstantide rühmitamiseks.
  • Saate neid isegi kasutada mingil hulgal nimeruumi juhtimiseks.

Nii et idiomaatilises C ++ programmis pole mingit põhjust kasutada täisarvukonstandi määramiseks #define . Isegi kui soovite jääda C-ühilduvaks (tehniliste nõuete tõttu, kuna panete vanakooli tööle või inimesed, kellega töötate, eelistavad seda nii), saate siiski kasutada enum ja peaksite tehke seda, selle asemel et kasutada koodi #define .

Tõstatate mõningaid suurepäraseid punkte (eriti massiivi piirangute osas - ma polnud veel aru saanud, et Arduino IDE standardkompilaator seda veel toetas). Pole päris õige öelda, et kompileerimise ajakonstant ei kasuta salvestusruumi, sest selle väärtus peab ikkagi esinema koodis (s.o. programmimälus, mitte SRAM-is) kõikjal, kus seda kasutatakse. See tähendab, et see mõjutab saadaolevat Flashi igat tüüpi, mis võtab rohkem ruumi kui osuti.
"nii et tegelikult on mitu selle vastu esitatud vastuväidet, millele on viidatud algses küsimuses" - miks need ei kehti algses küsimuses, kuna öeldakse, et need on C piirangud?
@Cybergibbons Arduino põhineb C ++-l, seega pole mulle selge, miks oleks asjakohased ainult C-piirangud (välja arvatud juhul, kui teie kood peab mingil põhjusel ka C-ga ühilduma).
@PeterR. Bloomfield, minu mõte konstantide kohta, mis ei vaja lisamälu, piirdus "const int" -ga. Keerukamate tüüpide puhul on teil õigus, et salvestusruumi võidakse eraldada, kuid sellegipoolest pole teil tõenäoliselt halvem kui "# define" -ga.
#2
+7
Peter Bloomfield
2014-02-28 16:40:06 UTC
view on stackexchange narkive permalink

MUUDA: mikrotreening annab suurepärase vastuse, mis parandab siin mõningaid minu punkte, eriti seoses mälukasutusega.


Nagu olete tuvastanud, on teatud olukordi kus olete sunnitud kasutama #define , sest kompilaator ei luba muutujat const . Samamoodi olete mõnes olukorras sunnitud kasutama muutujaid, näiteks kui vajate väärtuste massiivi (st teil ei saa olla massiivi #define).

Siiski on palju muid olukordi, kus pole tingimata ühte „õiget” vastust. Siin on mõned juhised, mida ma järgiksin:

tüübi ohutus
Üldiselt programmeerimise seisukohast eelistatakse tavaliselt muutujaid const (kus võimalik). Selle peamine põhjus on tüübi ohutus.

#define (eeltöötleja makro) kopeerib otsesõnalise väärtuse otse koodi igasse kohta, muutes iga kasutamise sõltumatuks. See võib hüpoteetiliselt põhjustada ebamäärasusi, sest tüüp võib lõppeda erinevalt olenevalt sellest, kuidas / kus seda kasutatakse.

Muutuja const on alati ainult üks tüüp, mis on määratud deklaratsiooniga ja lahendati initsialiseerimise käigus. See nõuab sageli selget näitlejat, enne kui see teisiti käitub (kuigi on erinevaid olukordi, kus seda saab turvaliselt kaudselt reklaamida). Vähemalt saab kompilaator (kui see on õigesti konfigureeritud) tüübiprobleemi ilmnemisel usaldusväärsema hoiatuse anda.

Selle võimalik lahendus on selgesõnalise ülekande või tüübi järelliite lisamine #define . Näiteks:

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

See lähenemisviis võib mõnel juhul siiski põhjustada süntaksiprobleeme kuidas seda kasutatakse.

Mälukasutus
Erinevalt üldotstarbelisest arvutamisest on Arduino-laadse asjaga tegelemisel mälu ilmselgelt esmaklassiline. Muutuja const ja #define kasutamine võib mõjutada andmete mällu salvestamise kohta, mis võib sundida teid üht või teist kasutama.

  • const muutujad (tavaliselt) salvestatakse SRAM-i koos kõigi teiste muutujatega.
  • #define -is kasutatud sõnasõnalised väärtused salvestada programmiruumi (Flash-mällu) koos visandiga.

(Pange tähele, et on palju asju, mis võivad täpselt mõjutada seda, kuidas ja kuhu midagi salvestatakse, näiteks kompilaator seadistamine ja optimeerimine.)

SRAMil ja Flashil on erinevad piirangud (nt Uno puhul vastavalt 2 ja 32 KB). Mõne rakenduse jaoks on SRAM-i lõppemine üsna lihtne, mistõttu võib olla kasulik mõne asja Flash-i viimine. Võimalik on ka vastupidine, kuigi tõenäoliselt vähem levinud.

PROGMEM
Tüüpturvalisuse eeliseid on võimalik saada, säilitades samal ajal andmeid ka programmiruumis (Flash). . Selleks kasutatakse märksõna PROGMEM . See ei tööta kõigi tüüpide puhul, kuid seda kasutatakse tavaliselt täisarvude või stringide massiivide korral.

dokumentatsioonis toodud üldine vorm on järgneb:

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

Stringitabelid on natuke keerulisemad, kuid dokumentatsioonis on täielikud üksikasjad.

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

Kindlat tüüpi muutujate puhul, mida täitmise ajal ei muudeta, võib tavaliselt kasutada kumbagi.

Muutujates sisalduvate digitaalsete PIN-koodide korral võivad mõlemad toimida - näiteks:

  const int ledPin = 13;  

Kuid on üks asjaolu, kus ma kasutan alati #define

Selle eesmärk on määratleda analoog-pin-numbrid, kuna need on tähtnumbrilised.

Muidugi võite kõvasti kodeerige PIN-koodid kogu koodis järgmiselt: a2 , a3 jne ja kompilaator teab, mida nendega teha. Siis, kui muudate nööpnõelu, tuleb iga kasutamist muuta.

Pealegi meeldib mulle alati, kui nööpnõelade definitsioonid on üleval kõik ühes kohas, nii et küsimus saab, mis tüüpi const sobiks PIN-koodile, mis on määratletud kui A5.

Nendel juhtudel kasutan alati #define

Pinge jagaja näide:

  //// read12 loeb 12V aku pinget //// SDsolar 8/8/18 // # define adcInput A5 // Pingejaoturi väljund tuleb analoog A5float R1 = 120000,0 korral; // R1 pingejaguri sisendi jaoks väliselt 0-15Vfloat R2 = 20000,0; R2 pingejaguri väljundiks ADCfloat vRef = 4,8; // 9 V Vcc-l läbib regulatorfloat vTmp, vIn; int väärtus; .. void setup () {.// lubage ADC-l stabiliseeruda = analogRead (adcPin); viivitus (50); väärtus = analogRead (adcPin); viivitus (50); väärtus = analogRead (adcPin); viivitus (50); väärtus = analogRead (adcPin); viivitus (50); väärtus = analogRead (adcPin); viivitus (50); väärtus = analogRead (adcPin) ;. void loop () {.. väärtus = analogRead (adcPin); vTmp = väärtus * (vRef / 1024,0); vIn = vTmp / (R2 / (R1 + R2)); . .  

Kõik seadistamise muutujad on üleval ja adcPin väärtust ei muudeta kunagi, välja arvatud kompileerimise ajal.

Pole muret, mis tüüpi adcPin on. Ja konstandi salvestamiseks ei kasutata binaarses lisas RAM-i.

Kompilaator asendab enne kompileerimist lihtsalt iga adcPin -i eksemplari stringiga A5 .


Seal on huvitav Arduino foorumi lõim, mis käsitleb muid otsustamisviise:

#define vs const muutuja (Arduino foorum)

Katkendid:

Koodi asendamine:

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

Silumiskood :

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

tõene ja false Booleanina RAM-i salvestamiseks

  Selle asemel, et kasutada "const bool true = 1;" ja sama väärtuseks "false" # define true (boolean) 1 # define false (tõeväärtus) 0  

Paljuski taandub isiklikele eelistustele, kuid on selge, et #define on mitmekülgsem.

Samadel tingimustel ei kasuta `const` rohkem RAM-i kui # # define. Ja analoognõelte jaoks määratleksin need kui "const uint8_t", kuigi "const int" ei teeks vahet.
Sa kirjutasid "_a` const` ei kasuta tegelikult rohkem RAM-i [...] enne, kui seda tegelikult kasutatakse_ ". Sa jätsid minu mõttest ilma: enamasti ei kasuta `const` RAM-i, isegi kui seda kasutatakse ,_. Seejärel: "_this on multipass-kompilaator_". Mis kõige tähtsam, see on _optimeeriv_ kompilaator. Võimaluse korral optimeeritakse konstandid [vahetuteks operandideks] (https://et.wikipedia.org/wiki/Addressing_mode#Immediate/literal).


See küsimus ja vastus tõlgiti automaatselt inglise keelest.Algne sisu on saadaval stackexchange-is, mida täname cc by-sa 3.0-litsentsi eest, mille all seda levitatakse.
Loading...