2010-08-08 6 views
5

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

इसके साथ, const सी में योग्य चर के कुछ वैध उपयोग क्या हैं? मैं उन कुछ लोगों के बारे में सोच सकता हूं जो हाल ही में कोड में आए हैं जिनके साथ मैंने काम किया है, लेकिन निश्चित रूप से अन्य होना चाहिए। , एक सूचक के लिए विशेष प्रहरी मूल्यों के रूप में उनके पते का उपयोग करना तो कभी नहीं के रूप में किसी अन्य सूचक के बराबर की तुलना करने के

  • : यहाँ मेरी सूची है। उदाहरण के लिए: char *sentinel(void) { static const char s; return &s; } या बस const char sentinel[1]; चूंकि हम केवल पते के बारे में परवाह करते हैं और इससे कोई फर्क नहीं पड़ता कि ऑब्जेक्ट को लिखा गया था, const का एकमात्र लाभ यह है कि संकलक आमतौर पर केवल पढ़ने-योग्य स्मृति में संग्रहीत करेंगे जो mmap द्वारा समर्थित है निष्पादन योग्य फ़ाइल या शून्य पृष्ठ की एक प्रति।

  • लाइब्रेरी के नए संस्करणों के साथ मूल्य बदल सकता है, जब पुस्तकालय (विशेष रूप से साझा पुस्तकालयों) से मूल्यों को निर्यात करने के लिए const योग्य चर का उपयोग करना। ऐसे मामलों में, लाइब्रेरी के इंटरफ़ेस हेडर में #define का उपयोग करना एक अच्छा दृष्टिकोण नहीं होगा क्योंकि यह उस लाइब्रेरी के विशेष संस्करण में स्थिरांक के मानों पर निर्भर करेगा जो इसे बनाया गया था।

  • बारीकी से पिछले उपयोग से संबंधित है, कभी कभी आप आवेदन करने के लिए एक पुस्तकालय से पूर्व निर्धारित वस्तुओं को बेनकाब करना चाहते हैं (सर्वोत्कृष्ट उदाहरण जा रहा है stdin, stdout, और मानक पुस्तकालय से stderr)। उस उदाहरण का उपयोग करते हुए, extern FILE __stdin; #define stdin (&__stdin) अधिकांश सिस्टम साझा पुस्तकालयों को लागू करने के तरीके के कारण बहुत खराब कार्यान्वयन होगा - आमतौर पर उन्हें पूरे ऑब्जेक्ट (यहां, FILE) की आवश्यकता होती है ताकि एप्लिकेशन लिंक होने पर निर्धारित पते पर कॉपी किया जा सके और एक निर्भरता लागू हो सके ऑब्जेक्ट का आकार (यदि पुस्तकालय पुनर्निर्मित किया जाता है और ऑब्जेक्ट का आकार बदलता है तो प्रोग्राम टूट जाएगा)। const पॉइंटर का उपयोग करना (पॉइंटर-टू -const नहीं) यहां सभी समस्याओं को हल करता है: extern FILE *const stdin;, जहां const पॉइंटर को प्री-डिफ़ाइंड ऑब्जेक्ट (जिसे स्वयं static घोषित किया गया है) पुस्तकालय में कहीं भी आंतरिक रूप से इंगित करने के लिए प्रारंभ किया गया है।

  • गणितीय कार्यों, चरित्र गुणों आदि के लिए लुकअप टेबल। यह स्पष्ट है कि मैं मूल रूप से शामिल करना भूल गया था, शायद इसलिए कि मैं व्यक्तिगत const अंकगणित/सूचक प्रकार के चर के बारे में सोच रहा था, वैसे ही सवाल विषय पहले आया था अप। मुझे याद रखने के लिए ट्रिगर करने के लिए एडन के लिए धन्यवाद।

  • लुकअप टेबल पर एक संस्करण के रूप में, राज्य मशीनों के कार्यान्वयन के रूप में। एडन ने एक उत्तर के रूप में एक विस्तृत उदाहरण प्रदान किया। मैंने पाया है कि वही अवधारणा अक्सर किसी भी फ़ंक्शन पॉइंटर्स के बिना बहुत उपयोगी होती है, यदि आप कुछ संख्यात्मक मानकों के संदर्भ में प्रत्येक राज्य से व्यवहार/संक्रमण को एन्कोड कर सकते हैं। बाकी

किसी को भी सी में const -qualified चर के लिए कुछ चतुर, व्यावहारिक उपयोग है?

उत्तर

6

const अक्सर माइक्रोकंट्रोलर के जीपीआईओ पिन पर मैपिंग के लिए एम्बेडेड प्रोग्रामिंग में उपयोग किया जाता है।उदाहरण के लिए:

 
typedef unsigned char const volatile * const tInPort; 
typedef unsigned char    * const tOutPort; 

tInPort my_input = (tInPort)0x00FA; 
tOutPort my_output = (tOutPort)0x00FC; 

इन दोनों गलती से सूचक ही जो कुछ मामलों में विनाशकारी हो सकता है परिवर्तित करने से प्रोग्रामर को रोकने के। tInPort घोषणा प्रोग्रामर को इनपुट मान बदलने से रोकती है।

volatile का उपयोग करके संकलक को यह मानने से रोकता है कि my_input के लिए सबसे हालिया मूल्य कैश में मौजूद होगा। तो my_input से कोई भी पढ़ा सीधे बस पर जाएगा और इसलिए हमेशा डिवाइस के आईओ पिन से पढ़ा जाएगा। जब प्रकार, एक प्रयोग करने योग्य शाब्दिक है कि नहीं है जैसे कि, एक नंबर के अलावा और कुछ

+1

हालांकि यह काम करता है, मुझे __ffine my_inport (* (unsigned char const volatile *) 0x00FA) पर लाभ नहीं दिखाई देता है और फिर 'c = my_inport' करने में सक्षम होने के नाते; मेरा संस्करण शायद संकलित होगा छोटे/अधिक कुशल कोड क्योंकि यह निरंतर अभिव्यक्ति है, जबकि 'const' चर के साथ संस्करण वास्तव में प्रत्येक बार उपयोग किए जाने पर अप्रत्यक्ष रूप से पता लोड कर सकता है। –

+4

यह मूल रूप से '# परिभाषित' स्थिरांक की उम्र पुरानी तर्क 'बनाम' स्थिरांक बनाम है। आम तौर पर, कोई भी दूसरे की तुलना में बेहतर या बुरा नहीं होता है। यह सिर्फ एक भाषा निर्माण है और एक प्रीप्रोसेसर कमांड है। अक्सर यह अनदेखा किया जाता है कि एक प्रोग्रामिंग भाषा किसी प्रोग्राम के व्याख्या के लिए निर्देशों के एक सेट से अधिक कुछ नहीं है। सी के मामले में, कार्यक्रम एक कंपाइलर है जिसका काम अनुकूलित असेंबली कोड का उत्पादन करना है। तो भाषा संरचनाओं का उपयोग करके इसे अधिक जानकारी प्रदान करनी चाहिए और इसे अधिक प्रासंगिक परिणाम देने की अनुमति देनी चाहिए। हालांकि, यह 'परिभाषित 'के साथ खो गया है। – DuFace

1
  • एक const चर उपयोगी है। पॉइंटर्स के लिए, आप पहले से ही एक उदाहरण देते हैं (stdin और सह) जहां आप #define का उपयोग कर सकते हैं, लेकिन आपको एक ऐसा लवण मिलेगा जिसे आसानी से असाइन किया जा सके। एक और उदाहरण struct और union प्रकार है, जिसके लिए कोई असाइन करने योग्य अक्षर नहीं हैं (केवल प्रारंभकर्ता)। उदाहरण के लिए विचार करें जटिल संख्याओं का एक उचित C89 कार्यान्वयन:

    typedef struct {double Re; double Im;} Complex; 
    const Complex Complex_0 = {0, 0}; 
    const Complex Complex_I = {0, 1}; /* etc. */ 
    
  • कभी कभी तुम सिर्फ एक संग्रहीत वस्तु और नहीं एक शाब्दिक है, क्योंकि आप एक बहुरूपी समारोह है कि एक void* और एक उम्मीद को डेटा पास करने की आवश्यकता की जरूरत है size_t। यहाँ cryptoki एपीआई (उर्फ पीकेसीएस # 11) से एक उदाहरण है: कई कार्यों एक बूलियन मान के लिए, CK_ATTRIBUTE की एक सरणी, जो मूल रूप से

    typedef struct { 
        CK_ATTRIBUTE_TYPE type; 
        void *pValue; 
        unsigned long ulValueLen; 
    } CK_ATTRIBUTE; 
    typedef unsigned char CK_BBOOL; 
    
    इसलिए अपने आवेदन में

    के रूप में परिभाषित किया गया है के रूप में पारित तर्कों की सूची की आवश्यकता होती है विशेषता एक बाइट 0 या 1 से युक्त करने के लिए एक सूचक पास करनी होगी:

    CK_BBOOL ck_false = 0; 
    CK_ATTRIBUTE template[] = { 
        {CKA_PRIVATE, &ck_false, sizeof(ck_false)}, 
    ... }; 
    
+0

@ आर ..: लेकिन निरंतर अभिव्यक्तियां ('# परिभाषित करें, मुझे लगता है?) बहुत विशिष्ट मामलों में केवल संभव है। – Gilles

+0

मैंने केवल उन मामलों का उल्लेख किया है जिन पर मैं सोच सकता हूं कि प्रश्न में '# परिभाषित' बेहतर काम नहीं करेगा। क्या आप कुछ लोगों के बारे में सोच सकते हैं? यही वह है जिसे मैं ढूंढ रहा हूं। –

+0

@ आर ..: ठीक है, मैं अब आपका ध्यान बेहतर समझता हूं, मैंने उदाहरणों के साथ अपना जवाब दोबारा लिखा है। – Gilles

1

const कुछ मामलों में जहां हम एक विशिष्ट तरीके से प्रत्यक्ष कोड को डेटा का उपयोग करने के लिए उपयोगी हो सकता है। उदाहरण के लिए, यहाँ एक पैटर्न का उपयोग मैं जब राज्य मशीनों लिख दिया गया है:

typedef enum { STATE1, STATE2, STATE3 } FsmState; 
struct { 
    FsmState State; 
    int (*Callback)(void *Arg); 
} const FsmCallbacks[] = { 
    { STATE1, State1Callback }, 
    { STATE2, State2Callback }, 
    { STATE3, State3Callback } 
}; 

int dispatch(FsmState State, void *Arg) { 
    int Index; 
    for(Index = 0; Index < sizeof(FsmCallbacks)/sizeof(FsmCallbacks[0]); Index++) 
     if(FsmCallbacks[Index].State == State) 
      return (*FsmCallbacks[Index].Callback)(Arg); 
} 

कुछ इस तरह के अनुरूप है:

int dispatch(FsmState State, void *Arg) { 
    switch(State) { 
     case STATE1: 
      return State1Callback(Arg); 
     case STATE2: 
      return State2Callback(Arg); 
     case STATE3: 
      return State3Callback(Arg); 
    } 
} 

लेकिन मेरे बनाए रखने के लिए के लिए आसान है, विशेष रूप से मामलों में जहां अधिक जटिल व्यवहार राज्यों से जुड़े उदाहरण के लिए, अगर हम एक राज्य विशेष बीच में बंद करें तंत्र करना चाहते थे, हम करने के लिए struct परिभाषा को बदल देंगे:

struct { 
    FsmState State; 
    int (*Callback)(void *Arg); 
    void (*Abort)(void *Arg); 
} const FsmCallbacks[] = {...}; 

और मैं नए राज्य के लिए दोनों abort और dispatch दिनचर्या को संशोधित करने की जरूरत नहीं है। तालिका को रनटाइम पर बदलने से रोकने के लिए मैं const का उपयोग करता हूं।

+0

हाँ मैंने राज्य मशीनों, लुकअप टेबल इत्यादि के लिए बड़े '- कॉन्स्ट' डेटा संरचनाओं को छोड़ दिया है। डुह .. –

2

उदाहरण के लिए:

void memset_type_thing(char *first, char *const last, const char value) { 
    while (first != last) *(first++) = value; 
} 

तथ्य यह है कि last एक निरंतर अभिव्यक्ति का हिस्सा नहीं हो सकता है न यहाँ और न ही वहाँ है। const टाइप सिस्टम का हिस्सा है, जो एक वैरिएबल को इंगित करने के लिए उपयोग किया जाता है जिसका मान नहीं बदलेगा। मेरे फ़ंक्शन में last के मान को संशोधित करने का कोई कारण नहीं है, इसलिए मैं इसे const घोषित करता हूं।

मैं इसे const घोषित करने के लिए परेशान नहीं कर सकता है, लेकिन फिर मैं एक स्थिर टाइप किया भाषा का उपयोग नहीं कर सकता है परेशान सब ;-)

+0

आपने उसी कारण से' मूल्य 'को 'const' होने की घोषणा क्यों नहीं की? – caf

+0

@caf: भूल गया, धन्यवाद। मैं विशेष रूप से 'अंतिम' स्थिरता को रक्षात्मक रूप से घोषित करना चाहता हूं, क्योंकि यह 'पहले' जैसा ही है, जिसे मैं संशोधित करने की योजना बना रहा हूं। चूंकि 'मान' एक अलग प्रकार का है, इसलिए इसे बनाने में थोड़ा कम लाभ होता है। फिर से, यह '* पहले' जैसा ही है, जिसे मैं * संशोधित कर रहा हूं। आप सही हैं, अतिरिक्त कोड के अलावा अन्य कोई कारण नहीं है।यह गैर-पैरामीटर स्वचालित चर पर लागू होता है - यदि आप 'em const बनाते हैं, तो कोड पढ़ने वाले किसी भी व्यक्ति के पास चिंता करने के लिए कम संभावित-परिवर्तनीय स्थिति होती है, और कुछ और के लिए उस मस्तिष्क का उपयोग कर सकती है। –

0

वे स्मृति-मैप की गई बाह्य उपकरणों या रजिस्टर के लिए इस्तेमाल किया जा सकता है कि उपयोगकर्ता कोड से नहीं बदला जा सकता , माइक्रोप्रोसेसर के केवल कुछ आंतरिक तंत्र। उदाहरण के लिए। पीआईसी 32 एमएक्स पर, प्रोग्राम स्टेटस को इंगित करने वाले कुछ रजिस्ट्रार const volatile योग्य हैं - ताकि आप उन्हें पढ़ सकें, और कंपाइलर ऑप्टिमाइज़ करने, कहने, बार-बार एक्सेस करने का प्रयास नहीं करेगा, लेकिन आपका कोड उन्हें नहीं लिख सकता है।

(मैं हाथ करने के लिए कोई कोड नहीं है, इसलिए मैं अभी एक अच्छा उदाहरण तलब नहीं कर सकते हैं।)

2

PC-lint warning 429 उम्मीद है कि एक आवंटित वस्तु के लिए एक स्थानीय सूचक का सेवन किया जाना चाहिए से इस प्रकार

  • यह एक "गंदे" कार्य करने के लिए पारित करके एक और सूचक या
  • करने के लिए इसे कॉपी करके यह या
  • मुक्त कराने के द्वारा या
  • (इस सूचक की "हिरासत में" संपत्ति पट्टी चाहिए) 10
  • इसे कॉलर को रिटर्न स्टेटमेंट या पास-बाय-पॉइंटर पैरामीटर के माध्यम से पास करके।

"गंदा" से मेरा मतलब है कि एक फ़ंक्शन जिसका संबंधित सूचक पैरामीटर गैर-आधार आधार प्रकार है। चेतावनी का वर्णन "गंदे" लेबल से स्ट्रैपी() जैसे लाइब्रेरी फ़ंक्शंस को स्पष्ट करता है, जाहिर है क्योंकि इस तरह के किसी भी लाइब्रेरी फ़ंक्शंस में ऑब्जेक्ट की स्वामित्व नहीं होती है।

तो पीसी-लिंट जैसे स्थिर विश्लेषण उपकरण का उपयोग करते समय, कॉल किए गए कार्यों के पैरामीटर के कॉन्स्ट क्वालीफायर स्थानीय रूप से आवंटित स्मृति क्षेत्रों को जिम्मेदार ठहराते हैं।

संबंधित मुद्दे