Kysymys:
Onko parempi käyttää vakioille #define tai const int?
Cybergibbons
2014-02-28 15:46:51 UTC
view on stackexchange narkive permalink

Arduino on pariton hybridi, jossa sulautetussa maailmassa käytetään perinteisesti C-ympäristöä joitain C ++ -toimintoja. Todellakin, suuri osa Arduino-koodista on kuitenkin hyvin C-tyyppinen.

C on perinteisesti käyttänyt vakioille #define s. Tähän on useita syitä:

  1. Et voi asettaa taulukon kokoja käyttämällä const int .
  2. Et voi käyttää const int tapauslausekkeen tunnisteina (vaikka tämä toimii joissakin kääntäjissä)
  3. const ei voi alustaa toisella const .

Voit tarkistaa tämän kysymyksen StackOverflow'sta saadaksesi lisää perusteluja.

Joten mitä meidän tulisi käyttää Arduinossa? Minulla on taipumus kohti #define , mutta näen osan koodista, joka käyttää const , ja osa, joka käyttää sekoitusta.

hyvä optimoija tekee siitä kiistanalaisen
Todella? En ymmärrä, kuinka kääntäjä aikoo ratkaista asioita, kuten tyyppiturvallisuutta, ei pysty käyttämään -merkkiä määrittämään matriisin pituuden ja niin edelleen.
Olen samaa mieltä. Lisäksi, jos tarkastelet alla olevaa vastaustani, osoitan, että on tilanteita, joissa et todellakaan tiedä, mitä tyyppiä käytetään, joten "# define" on ilmeinen valinta. Esimerkkini on nimetä analogiset nastat - kuten A5. Sille ei ole sopivaa tyyppiä, jota voitaisiin käyttää "const": na, joten ainoa vaihtoehto on käyttää "# define" ja antaa kääntäjän korvata se tekstinsyöttönä ennen merkityksen tulkintaa.
Kolme vastused:
#1
+23
microtherion
2014-02-28 21:05:37 UTC
view on stackexchange narkive permalink

On tärkeää huomata, että const int ei toimi identtisesti C: ssä ja C ++: ssa, joten itse asiassa useat sitä vastaan ​​esitetyistä väitteistä, joihin on viitattu alkuperäinen kysymys ja Peter Bloomfieldsin laaja vastaus eivät ole päteviä:

  • C ++: ssa const int -vakiot kokoavat aika-arvoja ja voidaan käyttää taulukon rajojen asettamiseksi tapausmerkinnöinä jne.
  • const int -vakiot eivät välttämättä vie mitään tallennustilaa. Ellet ota heidän osoitettaan tai julista heitä ulkoisiksi, heillä on yleensä vain kokoaika.

Kokonaisluvuille voi kuitenkin usein olla parempi käyttää (nimettyä tai tuntematonta). enum . Pidän tästä usein, koska:

  • Se on taaksepäin yhteensopiva C: n kanssa.
  • Se on melkein yhtä turvallista kuin const int (jokainen bitti yhtä tyyppinen kuin turvallinen) C ++ 11).
  • Se tarjoaa luonnollisen tavan ryhmittää toisiinsa liittyvät vakiot.
  • Voit käyttää niitä jopa jonkin verran nimiavaruuden hallintaan.

Joten idiomaattisessa C ++ -ohjelmassa ei ole mitään syytä käyttää #define -toimintoa kokonaisluvuvakion määrittelemiseen. Vaikka haluatkin pysyä C-yhteensopivana (teknisten vaatimusten vuoksi, koska aloitat vanhan koulun tai koska työskentelevät ihmiset pitävät siitä mieluummin), voit silti käyttää enum -tunnusta tee niin, älä käytä #define .

Esität joitain erinomaisia ​​kohtia (varsinkin matriisirajoista - en ollut vielä ymmärtänyt, että Arduino IDE: n vakiokääntäjä olisi vielä tuonut sitä). Ei ole aivan oikein sanoa, että kääntöaikavakio ei kuitenkaan käytä tallennustilaa, koska sen arvon on silti esiintyttävä koodissa (ts. Ohjelmamuistissa eikä SRAM: ssä) missä tahansa sitä käytetään. Tämä tarkoittaa, että se vaikuttaa käytettävissä olevaan Flashiin kaikentyyppisille, jotka vievät enemmän tilaa kuin osoitin.
"Joten itse asiassa useat sitä vastaan ​​esitetyistä väitteistä, joihin on viitattu alkuperäisessä kysymyksessä" - miksi ne eivät ole päteviä alkuperäisessä kysymyksessä, koska sanotaan, että nämä ovat C: n rajoituksia?
@Cybergibbons Arduino perustuu C ++: een, joten minulle ei ole selvää, miksi vain C-rajoitukset olisivat merkityksellisiä (ellei koodisi jostain syystä tarvitse olla yhteensopiva myös C: n kanssa).
@PeterR.Bloomfield, mielipiteeni vakioista, jotka eivät vaadi ylimääräistä tallennustilaa, rajoittui "const int": iin. Monimutkaisemmille tyypeille olet oikeassa, että tallennustila voidaan kohdistaa, mutta silti tuskin olet huonommassa asemassa kuin # -määrityksen kanssa.
#2
+7
Peter Bloomfield
2014-02-28 16:40:06 UTC
view on stackexchange narkive permalink

MUOKKAA: mikrotersio antaa erinomaisen vastauksen, joka korjaa joitain tässä esitettyjä kohtia, erityisesti muistin käytöstä.


Kuten olet huomannut, on olemassa tiettyjä tilanteita missä sinua pakotetaan käyttämään #define , koska kääntäjä ei salli muuttujaa const . Vastaavasti joissakin tilanteissa sinun on pakko käyttää muuttujia, kuten silloin, kun tarvitset joukkoa arvoja (eli et voi olla taulukkoa #define).

On kuitenkin monia muita tilanteita, joissa ei välttämättä ole yhtä "oikeaa" vastausta. Tässä on joitain ohjeita, joita noudatan:

Tyyppiturvallisuus
Yleisestä ohjelmoinnin näkökulmasta const -muuttujat ovat yleensä suositeltavia (missä mahdollista). Tärkein syy tähän on tyyppiturvallisuus.

#define (esiprosessorimakro) kopioi kirjaimellisen arvon suoraan koodin jokaiseen sijaintiin ja tekee jokaisesta käytöstä itsenäisen. Tämä voi hypoteettisesti johtaa epäselvyyksiin, koska tyyppi voi päätyä ratkaisemaan eri tavalla sen mukaan, miten / missä sitä käytetään.

const -muuttuja on aina vain yksi tyyppi, joka määritetään ilmoituksella ja ratkaistiin alustuksen aikana. Se vaatii usein nimenomaista näyttelijää, ennen kuin se käyttäytyy eri tavalla (vaikka on olemassa useita tilanteita, joissa sitä voidaan turvallisesti mainostaa epäsuorasti). Ainakin kääntäjä voi (jos se on määritetty oikein) antaa luotettavamman varoituksen, kun tyyppiongelma ilmenee.

Tämän mahdollinen kiertotapa on sisällyttää eksplisiittinen suoratoisto tai tyyppiliite #define . Esimerkiksi:

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

Tämä lähestymistapa saattaa aiheuttaa syntaksiongelmia joissakin tapauksissa, riippuen miten sitä käytetään.

Muistin käyttö
Toisin kuin yleiskäyttöinen tietojenkäsittely, muisti on tietysti huippuluokan käsiteltäessä jotain Arduinon kaltaista. Muuttujan const ja #define käyttäminen voi vaikuttaa siihen, mihin tiedot tallennetaan muistiin, mikä voi pakottaa käyttämään yhtä tai toista.

  • const -muuttujat (yleensä) tallennetaan SRAM: iin kaikkien muiden muuttujien kanssa.
  • #define -kohdassa käytetyt kirjaimelliset arvot tallennetaan usein tallennetaan ohjelmatilaan (Flash-muisti), itse luonnoksen viereen.

(Huomaa, että on monia asioita, jotka voivat vaikuttaa siihen, miten ja mihin jotain tallennetaan, kuten kääntäjä kokoonpano ja optimointi.)

SRAM: lla ja Flashilla on erilaiset rajoitukset (esim. 2 kt ja 32 kt Unolle). Joissakin sovelluksissa SRAM: n loppuminen on melko helppoa, joten voi olla hyödyllistä siirtää jotkut asiat Flashiin. Käänteinen on myös mahdollista, vaikkakin todennäköisesti harvinaisempi.

PROGMEM
On mahdollista saada tyyppiturvallisuuden edut tallentamalla tietoja myös ohjelmatilaan (Flash) . Tämä tehdään käyttämällä PROGMEM -avainsanaa. Se ei toimi kaikentyyppisissä, mutta sitä käytetään yleisesti kokonaisluku- tai merkkijonoryhmissä.

ohjeissa annettu yleinen muoto on seuraa:

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

Merkkijonotaulukot ovat hieman monimutkaisempia, mutta dokumentaatiossa on kaikki yksityiskohdat.

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

Määritetyn tyyppisille muuttujille, joita ei muuteta suorituksen aikana, voidaan yleensä käyttää kumpaakin.

Muuttujiin sisältyville digitaalisille pin-numeroille kumpikin voi toimia - kuten:

  const int ledPin = 13;  

Mutta on yksi olosuhde, jossa käytän aina #define

Se on määritellä analogiset pin-numerot, koska ne ovat aakkosnumeerisia.

Toki, voit koodaa pin-numerot muodossa a2 , a3 jne. kaikki koko ohjelmassa, ja kääntäjä tietää, mitä niiden kanssa tehdä. Sitten, jos vaihdat nastoja, kutakin käyttöä on muutettava.

Lisäksi haluan aina, että nastamääritykset ovat ylhäällä kaikki yhdessä paikassa, joten kysymykseksi tulee minkä tyyppinen const sopisi pinille, joka on määritelty nimellä A5.

Tällöin käytän aina #define

Esimerkki jännitteenjakajasta:

  //// read12 lukee 12 V: n akun jännitteen //// SDsolar 8/8/18 // # define adcInput A5 // Jännitteenjakajan lähtö tulee analogiseen A5float R1 = 120000,0; // R1 jännitteenjakajan tulolle ulkoisesta 0-15V-kelluvasta R2 = 20000,0; // R2 jännitteenjakajan ulostulolle ADCfloat vRef = 4,8; // 9 V Vcc: llä käy läpi regulatorfloat vTmp, vIn; int-arvo; .. void setup () {.// anna ADC: n stabiloitua arvo = analogRead (adcPin); viive (50); arvo = analoginen luku (adcPin); viive (50); arvo = analoginenLue (adcPin); viive (50); arvo = analoginen luku (adcPin); viive (50); arvo = analoginenLue (adcPin); viive (50); arvo = analogRead (adcPin) ;. void loop () {.. arvo = analogRead (adcPin); vTmp = arvo * (vRef / 1024,0); vIn = vTmp / (R2 / (R1 + R2)); . .  

Kaikki asetusmuuttujat ovat aivan yläreunassa, eikä adcPin -arvoon tule koskaan muutoksia, paitsi kääntämisajankohtana.

Ei huolta siitä, minkä tyyppinen adcPin on. Binaarissa ei käytetä ylimääräistä RAM-muistia vakion tallentamiseen.

Kääntäjä yksinkertaisesti korvaa kaikki adcPin -tapahtumat merkkijonolla A5 ennen kääntämistä.


On mielenkiintoinen Arduino-keskusteluketju, joka käsittelee muita tapoja päättää:

#define vs. const muuttuja (Arduino-keskustelupalsta)

Ote:

Koodin korvaaminen:

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

Virheenkorjauskoodi :

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

true ja false totuusarvoina RAM-muistin säästämiseksi

  Sen sijaan, että käytettäisiin "const bool true = 1;" ja sama sanoille "false" # define true (looginen) 1 # define false (looginen) 0  

Suuri osa siitä johtuu henkilökohtaisista mieltymyksistä, mutta on selvää, että #define on monipuolisempi.

Samoissa olosuhteissa "const" ei käytä enemmän RAM-muistia kuin "# define". Ja analogisten nastojen kohdalla määritellä ne nimellä `const uint8_t ', vaikka` const int` ei tekisi mitään eroa.
Kirjoitit "_a` const` ei oikeastaan ​​käytä enemmän RAM-muistia [...], ennen kuin sitä todella käytetään '. Hävitit mielipiteeni: "const" ei useimmiten käytä RAM-muistia, _ edes käytettäessä_. Sitten "_this on multipass-kääntäjä_". Mikä tärkeintä, se on _optimoiva_ kääntäjä. Aina kun mahdollista, vakiot optimoidaan [välittömiksi operandeiksi] (https://fi.wikipedia.org/wiki/Addressing_mode#Immediate/literal).


Tämä Q & A käännettiin automaattisesti englanniksi.Alkuperäinen sisältö on saatavilla stackexchange-palvelussa, jota kiitämme cc by-sa 3.0-lisenssistä, jolla sitä jaetaan.
Loading...