Sabitler için #define veya const int kullanmak daha mı iyidir?


26

Arduino, gömülü dünyada - geleneksel olarak bir C ortamında - bazı C ++ işlevlerinin kullanıldığı garip bir melezdir. Nitekim, bir çok Arduino kodu olsa da çok C gibidir.

C geleneksel #defineolarak sabitler için s kullandı . Bunun bir çok nedeni var:

  1. Kullanarak dizi boyutlarını ayarlayamazsınız const int.
  2. const intCase deyimi etiketleri olarak kullanamazsınız (bu, bazı derleyicilerde çalışsa da)
  3. İle constbaşkasını başlatamazsınız const.

Daha fazla muhakeme için bu soruyu StackOverflow'ta kontrol edebilirsiniz .

Peki, Arduino için ne kullanmalıyız? Doğru eğilimindeyim #define, ancak bazılarını kullanarak const, bazılarını bir karışım kullanarak görüyorum .


iyi bir eniyileyici tartışma konusu yapar
cırcır ucube

3
Gerçekten mi? Bir derleyicinin tür güvenliği gibi şeyleri nasıl çözeceğini, dizi uzunluğunu tanımlamak için vb. Kullanamayacağımı anlamıyorum.
Cybergibbons

Katılıyorum. Artı, aşağıdaki cevaba bakarsanız, hangi tipi kullanacağınızı gerçekten bilmediğiniz durumlar olduğunu gösterir, bu yüzden #definebariz seçenek. Benim örneğim Analog pinlere benziyor - A5 benzeri. Bunun için kullanılabilecek uygun bir tip yoktur, bu constyüzden tek seçenek a kullanmak #defineve anlamını yorumlamadan önce derleyicinin onu metin girişi olarak kullanmasına izin vermektir.
SDsolar

Yanıtlar:


21

O notta önemlidir const intgelmez değil böylece orijinal söz konusu ve Peter Bloomfields kapsamlı cevap ima edilmiştir buna karşı itiraz birkaç aslında geçerli değildir, C ++ ve C aynı şekilde davranması:

  • C ++, const intsabit derleme zaman değerleri ve edebilir vs. durumda etiket gibi, resim dizisi sınırları için kullanılabilir
  • const intSabitler mutlaka herhangi bir depolama alanı gerektirmez. Adreslerini almazsan ya da onları dışarıda ilan edemezsen, genellikle derleme zamanı varlığına sahip olurlar.

Bununla birlikte, tamsayı sabitleri için genellikle bir (adlandırılmış veya isimsiz) kullanmak tercih edilebilir enum. Sık sık bunu seviyorum çünkü:

  • C ile geriye doğru uyumludur.
  • Neredeyse güvenli bir türdür const int(C ++ 11'de her bit kadar güvenli).
  • İlgili sabitleri gruplamanın doğal bir yolunu sağlar.
  • Bunları bir miktar isim kontrolü için bile kullanabilirsiniz.

Yani bir deyimsel C ++ programında, #definebir tamsayı sabiti tanımlamak için kullanılacak hiçbir neden yoktur . C ile uyumlu kalmak isteseniz bile (teknik gereklilikler nedeniyle, eski okulda okuduğunuz için ya da birlikte çalıştığınız insanlar bu şekilde tercih ettikleri için), kullanmak enumyerine yine de kullanabilirsiniz #define.


2
Bazı mükemmel noktaları (özellikle dizi limitleri hakkında) yükseltiyorsunuz - Arduino IDE'nin desteklediği standart derleyiciyi henüz anlamadım). Bir derleme zamanı sabitinin depolama alanı kullanmadığını söylemek doğru değildir, çünkü değerinin hala kullanıldığı yerde herhangi bir yerde kodda (yani SRAM yerine program belleği) olması gerekir. Bu, bir işaretçiden daha fazla yer kaplayan herhangi bir tür için mevcut Flash'ı etkilediği anlamına gelir.
Peter Bloomfield

1
“öyleyse, aslında asıl soruya itiraz edilen itirazların birçoğu” - asıl soruda neden geçerli değiller ki, bunların C'nin kısıtları olduğu belirtildi.
Cybergibbons

@Cybergibbons Arduino, C ++ 'a dayanıyor, bu yüzden bana neden sadece C kısıtlamalarının uygun olacağı belli değil (nedense kodunuzun C ile de uyumlu olması gerekmiyorsa).
mikrotherion

3
@ PeterR.Bloomfield, fazladan depolama gerektirmeyen sabitler hakkındaki noktam sınırlandırıldı const int. Daha karmaşık türler için, depolama alanının tahsis edilebileceği konusunda haklısınız, ancak öyle olsa bile, a'dan daha kötü durumda olmanız pek mümkün değil #define.
mikrotherion

7

EDIT: microtherion, burada özellikle hafıza kullanımıyla ilgili bazı noktaları düzelten mükemmel bir cevap veriyor.


Belirlediğiniz gibi, a'yı kullanmaya zorlandığınız durumlar vardır #define, çünkü derleyici bir constdeğişkene izin vermez . Benzer şekilde, bazı durumlarda, bir değerler dizisine ihtiyaç duyduğunuzda (yani bir diziye sahip olamayacağınız gibi) değişkenleri kullanmak zorunda kalırsınız #define.

Ancak, mutlaka tek bir 'doğru' cevabın olmadığı birçok başka durum vardır. İşte izleyeceğim bazı kurallar:

Tip güvenliği
Genel bir programlama bakış açısından constdeğişkenler genellikle tercih edilir (mümkün olduğunda). Bunun ana nedeni tip güvenliğidir.

A #define(önişlemci makrosu), değişmez değeri doğrudan koddaki her konuma kopyalayarak her bir kullanımı bağımsız kılar. Bu varsayımsal olarak belirsizliklere neden olabilir, çünkü türün nasıl / nerede kullanıldığına bağlı olarak farklı şekilde çözülmesiyle sonuçlanabilir.

Bir constdeğişken, bildirimi tarafından belirlenen ve başlatma sırasında çözülen, yalnızca bir türdür. Farklı davranışlarda bulunmadan önce genellikle açık bir oyuncu kadrosu gerektirecektir (her ne kadar güvenli bir şekilde tip tanıtımı yapılabileceği çeşitli durumlar olsa da). En azından, derleyici (doğru yapılandırılmışsa) bir tip sorunu meydana geldiğinde daha güvenilir bir uyarı verebilir.

Bunun için olası bir geçici çözüm, a içine açık bir döküm veya tür sonekini eklemektir #define. Örneğin:

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

Bu yaklaşım, bazı durumlarda, nasıl kullanıldığına bağlı olarak sözdizimi sorunlarına neden olabilir.

Bellek kullanımı
Genel amaçlı bilgi işlemlerin aksine, bellek bir Arduino gibi bir şeyle uğraşırken çok önemlidir. A constdeğişkenine karşı bir değişken kullanmak #define, verilerin bellekte nerede saklandığını etkileyebilir; bu da sizi birini veya diğerini kullanmaya zorlayabilir.

  • const değişkenler (genellikle) SRAM'da ve diğer tüm değişkenlerle birlikte depolanır.
  • Kullanılan hazır değerler #definegenellikle çizimin yanında program alanında (Flash bellek) saklanır.

(Derleyici yapılandırması ve optimizasyonu gibi bir şeyin tam olarak nasıl ve nerede saklandığını etkileyebilecek çeşitli şeylerin olduğunu unutmayın.)

SRAM ve Flash'ın farklı sınırlamaları vardır (örn. Uno için sırasıyla 2 KB ve 32 KB). Bazı uygulamalar için, SRAM'in tükenmesi oldukça kolaydır, bu nedenle bazı şeyleri Flash'a kaydırmak faydalı olabilir. Tersi de mümkündür, ancak muhtemelen daha az yaygındır.

PROGMEM
Veriyi program alanına (Flash) kaydederken tip güvenliğinden faydalanmak da mümkündür. Bu PROGMEManahtar kelime kullanılarak yapılır . Tüm türler için işe yaramaz, ancak genellikle tam sayı veya dizge dizileri için kullanılır.

Belgelerde verilen genel form aşağıdaki gibidir:

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

Dize tabloları biraz daha karmaşık, ancak belgelerin tüm detayları var.


1

Uygulama sırasında değiştirilmeyen belirli bir tipteki değişkenler için, genellikle de kullanılabilir.

Değişkenlerde bulunan Dijital pin numaraları için, ya çalışabilir - örneğin:

const int ledPin = 13;

Ama her zaman kullandığım bir durum var #define

Analog pin numaralarını tanımlamak içindir, çünkü bunlar alfanümeriktir.

Tabii, siz gömmemeli pin numarası can a2, a3vb tüm program boyunca ve derleyici onlarla ne yapacağını bilecek. Sonra pimleri değiştirirseniz, her kullanımın değiştirilmesi gerekecektir.

Dahası, pin tanımlarımı her zaman en üstte bir yerde tutmak isterim, bu nedenle soru, hangi tipte constbir pin için uygun olacağını gösterir A5.

Bu durumlarda her zaman kullanırım #define

Voltage Divider Örneği:

//
//  read12     Reads Voltage of 12V Battery
//
//        SDsolar      8/8/18
//
#define adcInput A5    // Voltage divider output comes in on Analog A5
float R1 = 120000.0;   // R1 for voltage divider input from external 0-15V
float R2 =  20000.0;   // R2 for voltage divider output to ADC
float vRef = 4.8;      // 9V on Vcc goes through the regulator
float vTmp, vIn;
int value;
.
.
void setup() {
.
// allow ADC to stabilize
value=analogRead(adcPin); delay(50); value=analogRead(adcPin); delay(50);
value=analogRead(adcPin); delay(50); value=analogRead(adcPin); delay(50);
value=analogRead(adcPin); delay(50); value=analogRead(adcPin);
.
void loop () {
.
.
  value=analogRead(adcPin);
  vTmp = value * ( vRef / 1024.0 );  
  vIn = vTmp / (R2/(R1+R2)); 
 .
 .

Tüm kurulum değişkenleri adcPinen üsttedir ve derleme zamanı dışında hiçbir zaman değerinde bir değişiklik olmaz .

Ne tür adcPinolduğu hakkında endişelenme . Ve bir sabit depolamak için ikili dosyada fazladan RAM kullanılmaz.

Derleyici derlemeden önce adcPindizenin her örneğini değiştirir A5.


Karar vermenin başka yollarını tartışan ilginç bir Arduino Forumu başlığı var:

#define vs. const değişkeni (Arduino forumu)

Excertps:

Kod değişimi:

#define FOREVER for( ; ; )

FOREVER
 {
 if (serial.available() > 0)
   ...
 }

Hata ayıklama kodu:

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

RAM'den tasarruf etmek için trueve falseBoole olarak tanımlamak

Instead of using `const bool true = 1;` and same for `false`

#define true (boolean)1
#define false (boolean)0

Birçoğunun kişisel tercihi var, ancak bunun #definedaha çok yönlü olduğu açık .


Aynı durumlarda, bir consta'dan daha fazla RAM kullanmaz #define. Analog pinler için, onları bir fark const uint8_tyaratmamasına rağmen tanımlayacağım const int.
Edgar Bonet

A constgerçekten kullanılıncaya kadar daha fazla RAM kullanmıyor […]yazdınız . Amacımı kaçırdınız: çoğu zaman a constkullanıldığında bile RAM kullanmıyor . Sonra, “ bu bir çok yollu derleyici ”. En önemlisi, bu bir optimizasyon derleyici. Mümkün olduğunda, sabitler anında işlenenlere dönüştürülür .
Edgar Bonet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.