क्या कॉन्स्टेंट के लिए #define या कॉन्स्टेंट इंट का इस्तेमाल करना बेहतर है?


26

Arduino एक विषम हाइब्रिड है, जहां एम्बेडेड दुनिया में कुछ C ++ कार्यक्षमता का उपयोग किया जाता है - पारंपरिक रूप से एक C वातावरण। वास्तव में, Arduino कोड का एक बहुत हालांकि सी की तरह है।

सी ने पारंपरिक रूप #defineसे स्थिरांक के लिए एस का उपयोग किया है। इसके कई कारण हैं:

  1. आप सरणी आकारों का उपयोग करके सेट नहीं कर सकते const int
  2. आप const intकेस स्टेटमेंट लेबल के रूप में उपयोग नहीं कर सकते (हालांकि यह कुछ संकलक में काम करता है)
  3. आप constदूसरे के साथ आरंभ नहीं कर सकते const

अधिक तर्क के लिए आप StackOverflow पर इस प्रश्न की जाँच कर सकते हैं ।

तो, हमें Arduino के लिए क्या उपयोग करना चाहिए? मैं ओर जाता हूं #define, लेकिन मैं कुछ कोड का उपयोग करता हूं constऔर कुछ मिश्रण का उपयोग करता हूं ।


एक अच्छा ऑप्टिमाइज़र इसे मूट रेंडर करेगा
शाफ़्ट फ्रीक

3
वास्तव में? मैं यह नहीं देखता कि एक कंपाइलर किस प्रकार की सुरक्षा जैसी चीजों को हल करने जा रहा है, सरणी लंबाई और इतने पर परिभाषित करने के लिए उपयोग करने में सक्षम नहीं है।
Cybergibbons

मैं सहमत हूँ। इसके अलावा, यदि आप नीचे मेरे उत्तर को देखते हैं, तो मैं दिखाता हूं कि ऐसी परिस्थितियां हैं जब आप वास्तव में नहीं जानते हैं कि किस प्रकार का उपयोग करना है, इसलिए #defineयह स्पष्ट विकल्प है। मेरा उदाहरण एनालॉग पिन के नामकरण में है - जैसे A5। इसके लिए कोई उपयुक्त प्रकार नहीं है जिसका उपयोग constकेवल एक विकल्प के रूप में किया जा सकता है #defineऔर अर्थ की व्याख्या करने से पहले कंपाइलर को पाठ इनपुट के रूप में स्थानापन्न करना है।
एसडीसोलर

जवाबों:


21

यह नोट करना महत्वपूर्ण है कि const intहै नहीं सी में और सी में समान रूप से व्यवहार ++, तो तथ्य यह है इसके खिलाफ आपत्ति है कि मूल प्रश्न में और पीटर Bloomfields के व्यापक जवाब में संकेत दिया है के कई में मान्य नहीं हैं:

  • C ++ में, const intस्थिरांक समय मान संकलित करते हैं और इसका उपयोग सरणी सीमाएं सेट करने के लिए किया जा सकता है, जैसे केस लेबल, आदि।
  • const intस्थिरांक जरूरी किसी भी भंडारण पर कब्जा नहीं करते हैं। जब तक आप उनका पता नहीं लेते हैं या उन्हें बाहरी घोषित नहीं करते हैं, तब तक वे आम तौर पर एक संकलन समय अस्तित्व में होंगे।

हालाँकि, पूर्णांक स्थिरांक के लिए, अक्सर (नाम या अनाम) का उपयोग करना बेहतर हो सकता है enum। मुझे अक्सर यह पसंद है क्योंकि:

  • यह सी के साथ संगत है।
  • यह लगभग उतना ही सुरक्षित है जितना const int(C ++ 11 में हर बिट सुरक्षित प्रकार)।
  • यह संबंधित स्थिरांक का एक स्वाभाविक तरीका प्रदान करता है।
  • तुम भी उन्हें नामस्थान नियंत्रण की कुछ राशि के लिए उपयोग कर सकते हैं।

तो एक मुहावरेदार सी ++ प्रोग्राम में, #defineपूर्णांक स्थिरांक को परिभाषित करने के लिए उपयोग करने का कोई कारण नहीं है । यहां तक ​​कि अगर आप सी संगत रहना चाहते हैं (तकनीकी आवश्यकताओं के कारण, क्योंकि आप पुराने स्कूल को लात मार रहे हैं, या क्योंकि आप जिन लोगों के साथ काम करते हैं, वे इसे पसंद करते हैं), आप अभी भी उपयोग कर सकते हैं enumऔर ऐसा करना चाहिए, बजाय उपयोग के #define


2
आप कुछ उत्कृष्ट बिंदुओं को उठाते हैं (विशेष रूप से सरणी सीमाओं के बारे में - मुझे अरुदिनो आईडीई के साथ मानक कंपाइलर का एहसास नहीं हुआ था जो अभी तक समर्थित है)। यह कहना बिल्कुल सही नहीं है कि एक संकलन-समय स्थिरांक का उपयोग कोई भंडारण नहीं करता है, क्योंकि इसका मूल्य अभी भी कोड में है (यानी SRAM के बजाय प्रोग्राम मेमोरी) जहां भी इसका उपयोग किया जाता है। इसका मतलब है कि यह किसी भी प्रकार के लिए उपलब्ध फ्लैश को प्रभावित करता है जो एक पॉइंटर से अधिक जगह लेता है।
पीटर ब्लूमफील्ड

1
"इसलिए वास्तव में इसके खिलाफ कई आपत्तियाँ जो मूल प्रश्न में हल की गई हैं" - वे मूल प्रश्न में मान्य क्यों नहीं हैं, क्योंकि यह कहा गया है कि ये C की बाधाएं हैं?
Cybergibbons

@Cybergibbons Arduino C ++ पर आधारित है, इसलिए यह मेरे लिए स्पष्ट नहीं है कि C केवल बाधाएं क्यों उचित होंगी (जब तक कि किसी कारण से आपके कोड को C के साथ भी संगत होने की आवश्यकता नहीं है)।
माइक्रोथेरियन

3
@ PeterR.Bloomfield, स्थिरांक के बारे में मेरी बात अतिरिक्त भंडारण की आवश्यकता नहीं थी const int। अधिक जटिल प्रकारों के लिए, आप सही हैं कि भंडारण आवंटित किया जा सकता है, लेकिन फिर भी, आप के साथ खराब होने की संभावना नहीं है #define
माइक्रोथेरियन

7

संपादित करें: सूक्ष्मता एक उत्कृष्ट उत्तर देती है जो यहां मेरे कुछ बिंदुओं को सही करती है, विशेष रूप से स्मृति उपयोग के बारे में।


जैसा कि आप पहचान चुके हैं, कुछ ऐसी स्थितियाँ हैं जहाँ आप एक का उपयोग करने के लिए मजबूर हैं #define, क्योंकि कंपाइलर एक constचर की अनुमति नहीं देगा । इसी तरह, कुछ स्थितियों में आपको चर का उपयोग करने के लिए मजबूर किया जाता है, जैसे कि जब आपको मूल्यों की एक सरणी की आवश्यकता होती है (यानी आपके पास सरणी नहीं हो सकती #define)।

हालाँकि, कई अन्य स्थितियाँ हैं जहाँ आवश्यक रूप से एक भी 'सही' उत्तर नहीं है। यहां कुछ दिशानिर्देश दिए गए हैं जिनका मैं पालन करूंगा:

प्रकार की सुरक्षा
एक सामान्य प्रोग्रामिंग बिंदु से देखने के लिए, constचर आमतौर पर बेहतर होते हैं (जहां संभव हो)। इसका मुख्य कारण टाइप-सेफ्टी है।

A #define(प्रीप्रोसेसर मैक्रो) सीधे कोड में प्रत्येक स्थान पर शाब्दिक मूल्य को कॉपी करता है, जिससे प्रत्येक उपयोग स्वतंत्र हो जाता है। यह काल्पनिक रूप से अस्पष्टता का परिणाम हो सकता है, क्योंकि इसका उपयोग कैसे / कहां किया जाता है, इसके आधार पर प्रकार अलग-अलग हल हो सकते हैं।

एक constचर केवल एक प्रकार का होता है, जो इसकी घोषणा से निर्धारित होता है, और आरंभीकरण के दौरान हल किया जाता है। इसे अलग-अलग व्यवहार करने से पहले अक्सर एक स्पष्ट कलाकारों की आवश्यकता होगी (हालांकि विभिन्न परिस्थितियां हैं जहां इसे सुरक्षित रूप से टाइप-प्रमोशन किया जा सकता है)। बहुत कम से कम, कंपाइलर (यदि सही तरीके से कॉन्फ़िगर किया गया है) एक प्रकार की समस्या होने पर अधिक विश्वसनीय चेतावनी का उत्सर्जन कर सकता है।

इसके लिए एक संभावित समाधान एक स्पष्ट कलाकारों या एक प्रकार के प्रत्यय को शामिल करना है #define। उदाहरण के लिए:

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

यह दृष्टिकोण संभावित रूप से कुछ मामलों में वाक्यविन्यास समस्याओं का कारण बन सकता है, हालांकि यह कैसे उपयोग किया जाता है पर निर्भर करता है।

मेमोरी का उपयोग
सामान्य उद्देश्य कंप्यूटिंग के विपरीत, मेमोरी स्पष्ट रूप से एक प्रीमियम पर होती है जब एक Arduino जैसी किसी चीज के साथ व्यवहार किया जाता है। constवैरिएबल बनाम का उपयोग करने से #defineयह प्रभावित हो सकता है कि डेटा मेमोरी में कहाँ संग्रहीत है, जो आपको एक या दूसरे का उपयोग करने के लिए मजबूर कर सकता है।

  • const चर (आमतौर पर) SRAM में संग्रहीत किए जाएंगे, अन्य सभी चर के साथ।
  • शाब्दिक मूल्यों का उपयोग #defineअक्सर स्केच के साथ प्रोग्राम स्पेस (फ्लैश मेमोरी) में किया जाता है।

(ध्यान दें कि विभिन्न चीजें हैं जो वास्तव में कैसे और जहां कुछ संग्रहीत की जाती हैं, जैसे संकलक विन्यास और अनुकूलन को प्रभावित कर सकती हैं।)

SRAM और Flash की अलग-अलग सीमाएँ हैं (उदाहरण के लिए क्रमशः 2 KB और 32 KB)। कुछ अनुप्रयोगों के लिए, SRAM से बाहर भागना काफी आसान है, इसलिए यह कुछ चीजों को फ्लैश में स्थानांतरित करने में मददगार हो सकता है। रिवर्स भी संभव है, हालांकि शायद कम आम है।

PROGMEM
प्रोग्राम स्पेस (फ़्लैश) में डेटा संग्रहीत करते समय टाइप-सेफ्टी का लाभ प्राप्त करना संभव है। यह PROGMEMकीवर्ड का उपयोग करके किया जाता है । यह सभी प्रकारों के लिए काम नहीं करता है, लेकिन आमतौर पर इसका उपयोग पूर्णांक या स्ट्रिंग्स के सरणियों के लिए किया जाता है।

में दिया गया सामान्य रूप प्रलेखनरूप इस प्रकार हैं:

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

स्ट्रिंग टेबल कुछ अधिक जटिल हैं, लेकिन प्रलेखन में पूरी जानकारी है।


1

एक निर्दिष्ट प्रकार के चर के लिए जो निष्पादन के दौरान बदल नहीं रहे हैं, या तो आमतौर पर इस्तेमाल किया जा सकता है।

चर में निहित डिजिटल पिन संख्याओं के लिए, या तो काम कर सकते हैं - जैसे:

const int ledPin = 13;

लेकिन एक परिस्थिति है जहाँ मैं हमेशा उपयोग करता हूँ #define

यह एनालॉग पिन नंबर को परिभाषित करना है, क्योंकि वे अल्फ़ान्यूमेरिक हैं।

निश्चित रूप से, आप पिन नंबरों को हार्ड-कोड कर सकते हैं a2,a3 पूरे कार्यक्रम आदि और कंपाइलर को पता चल जाएगा कि उनके साथ क्या करना है। फिर यदि आप पिन बदलते हैं तो प्रत्येक उपयोग को बदलना होगा।

इसके अलावा, मैं हमेशा अपनी पिन परिभाषाओं को एक ही स्थान पर शीर्ष पर रखना पसंद करता हूं, इसलिए यह प्रश्न बन जाता है कि constपिन को परिभाषित करने के लिए किस प्रकार का उपयुक्त होगाA5

उन मामलों में मैं हमेशा उपयोग करता हूं #define

वोल्टेज विभक्त उदाहरण:

//
//  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)); 
 .
 .

सभी सेटअप चर शीर्ष पर सही हैं और adcPinसंकलन समय पर छोड़कर कभी भी मूल्य में कोई बदलाव नहीं होगा ।

किस प्रकार का adcPinहै, इसकी कोई चिंता नहीं । और निरंतर स्टोर करने के लिए बाइनरी में किसी भी अतिरिक्त रैम का उपयोग नहीं किया जाता है।

संकलक बस संकलन से पहले adcPinस्ट्रिंग के साथ प्रत्येक उदाहरण को प्रतिस्थापित करता है A5


एक दिलचस्प Arduino फोरम थ्रेड है जो निर्णय लेने के अन्य तरीकों पर चर्चा करता है:

#define बनाम कास्ट वैरिएबल (Arduino फोरम)

Excertps:

कोड प्रतिस्थापन:

#define FOREVER for( ; ; )

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

डिबगिंग कोड:

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

परिभाषित trueऔर falseराम को बचाने के लिए बूलियन के रूप में

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

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

इसका बहुत सा हिस्सा व्यक्तिगत पसंद में आता है, हालांकि यह स्पष्ट है कि #defineयह अधिक बहुमुखी है।


एक ही परिस्थितियों में, एक constसे अधिक रैम का उपयोग नहीं करेगा #define। और एनालॉग पिन के लिए, मैं उन्हें परिभाषित करता हूं const uint8_t, हालांकि const intइससे कोई फर्क नहीं पड़ेगा।
एडगर बोनट

आप "लिखा एक constवास्तव में अधिक रैम [...] जब तक यह वास्तव में प्रयोग किया जाता है का उपयोग नहीं करता "। आपने मेरी बात को याद किया: ज्यादातर समय, constरैम का उपयोग नहीं करता है, तब भी जब इसका उपयोग किया जाता है । फिर, " यह एक बहु संकलक है "। सबसे महत्वपूर्ण बात, यह एक अनुकूलन कंपाइलर है। जब भी संभव हो, स्थिरांक तत्काल ऑपरेंड में अनुकूलित हो जाते हैं ।
एडगर बोनट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.