कॉन्स्ट पॉइंटर्स की क्या बात है?


149

मैं नक्षत्रों के बारे में मानों के बारे में बात नहीं कर रहा हूं, लेकिन खुद को इंगित करता हूं।

मैं C और C ++ को बहुत ही मूल सामग्री से परे सीख रहा हूं और आज तक मैंने महसूस किया है कि पॉइंटर्स को फंक्शंस द्वारा पास किया जाता है, जो समझ में आता है। इसका मतलब यह है कि एक फ़ंक्शन के अंदर मैं कॉपी किए गए पॉइंटर को कॉल करने वाले से मूल पॉइंटर को प्रभावित किए बिना कुछ अन्य मूल्य पर बना सकता हूं।

तो एक समारोह हेडर होने का क्या मतलब है जो कहता है:

void foo(int* const ptr);

इस तरह के एक फंक्शन के अंदर आप ptr को कुछ और नहीं बना सकते क्योंकि यह const है और आप नहीं चाहते कि इसे संशोधित किया जाए, लेकिन यह एक फंक्शन है:

void foo(int* ptr);

काम तो करता ही है! क्योंकि पॉइंटर को वैसे भी कॉपी किया जाता है और कॉलर में पॉइंटर को प्रभावित नहीं किया जाता है भले ही आप कॉपी को संशोधित करते हों। तो कब्ज का फायदा क्या है?


29
यदि आप गारंटी देना चाहते हैं, तो संकलन समय पर, कि सूचक को और कुछ करने के लिए संकेत करने के लिए संशोधित नहीं किया जाना चाहिए?
प्लैटिनम एज़्योर

25
बिल्कुल किसी भी constपैरामीटर के समान बिंदु ।
डेविड हेफर्नन

2
@ प्लैटिनम एज़्योर, मेरे पास कुछ वर्षों का अनुभव प्रोग्रामिंग है, फिर भी मेरे सॉफ्टवेयर प्रथाओं का अभाव है। मुझे खुशी है कि मैंने यह सवाल किया क्योंकि मुझे कंपाइलर पॉइंट को अपनी लॉजिक एरर्स बनाने की जरूरत कभी महसूस नहीं हुई। इस प्रश्न को प्रस्तुत करते समय मेरी प्रारंभिक प्रतिक्रिया थी कि "पॉइंटर को संशोधित करने पर मुझे क्यों ध्यान देना चाहिए ?, यह कॉलर को प्रभावित नहीं करेगा"। जब तक मैं उन सभी उत्तरों को नहीं पढ़ लेता, जिन्हें मैं समझता था कि मुझे परवाह करनी चाहिए क्योंकि अगर मैं इसे संशोधित करने की कोशिश करता हूं, तो मुझे और मेरे कोडिंग तर्क गलत हैं (या तारांकन याद आ रही है जैसे एक टाइपो था) और मुझे एहसास नहीं हुआ होगा कि कंपाइलर नहीं था ' t मुझे बताओ
आर रुइज

3
@ R.Ruiz। निस्संदेह, यहां तक ​​कि हम में से सबसे अनुभवी भी शुद्धता की अतिरिक्त गारंटी के साथ कर सकते हैं। क्योंकि सॉफ्टवेयर इंजीनियरिंग इंजीनियरिंग का एक रूप है जहां थोड़ा और अधिक स्वीकार्य हो सकता है (यादृच्छिक HTTP 502s, धीमी गति से कनेक्शन, लोड करने में विफल होने वाली सामयिक छवि असाधारण अवसर नहीं हैं, लेकिन हवाई जहाज में विफल होने वाला इंजन अस्वीकार्य और संभावित रूप से गंभीर है, कभी-कभी लोग अनुचित जल्दबाजी के साथ कार्यक्रम करते हैं। एक ही तर्क जो लेखन इकाई परीक्षणों को सही ठहराता है, const-मूल्यांकन गारंटी के उपयोग की व्याख्या करेगा । यह सिर्फ हमें यह सुनिश्चित करता है कि हमारा कोड वास्तव में सही है।
प्लैटिनम एज़्योर

3
एक संबंधित प्रश्न: stackoverflow.com/questions/219914/…
Raedwald

जवाबों:


207

const एक उपकरण है जिसे आपको एक बहुत ही महत्वपूर्ण C ++ कॉन्सेप्ट के अनुसरण में उपयोग करना चाहिए:

संकलक पर बग को ढूंढें, रन-टाइम के बजाय, संकलक को लागू करने के लिए जो आप का मतलब है।

भले ही यह कार्यक्षमता में बदलाव नहीं करता है, लेकिन constजब आप ऐसा करने का मतलब नहीं करते हैं, तो आप एक कंपाइलर त्रुटि जोड़ते हैं। निम्नलिखित टाइपो की कल्पना करें:

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

यदि आप उपयोग करते हैं int* const, तो यह एक संकलक त्रुटि उत्पन्न करेगा क्योंकि आप मान बदल रहे हैं ptr। वाक्यविन्यास के माध्यम से प्रतिबंध जोड़ना सामान्य रूप से एक अच्छी बात है। बस इसे बहुत दूर न ले जाएं - आपके द्वारा दिया गया उदाहरण एक ऐसा मामला है जहां ज्यादातर लोग उपयोग करने से परेशान नहीं होते हैं const


8
धन्यवाद, यह एक उत्तर है जो मुझे आश्वस्त करता है। आप कास्ट करते हैं ताकि कंपाइलर आपको अपने असाइनमेंट की त्रुटियों के बारे में आगाह करे। आपका उदाहरण इस अवधारणा को स्पष्ट करने के लिए एकदम सही है क्योंकि यह संकेतकर्ताओं के साथ एक सामान्य गलती है। आपके मुकाबले!
आर रुइज

25
"कम्पाइलर हेल्प यू हेल्प" यह मंत्र है जिसके लिए मैं सामान्य रूप से जप करता हूँ।
फ्लेक्सो

1
+1, लेकिन यहां एक दिलचस्प मामला है जहां यह तर्क-संगत है: stackoverflow.com/questions/6305906/…
Raedwald

3
तो यह वही जवाब है जो आप "क्यों चर को निजी बनाते हैं, जब आप सिर्फ कक्षा के बाहर उनका उपयोग नहीं कर सकते।"
ली लोविएरे

यह थोड़ा बंद विषय हो सकता है, लेकिन यह कॉन्स्टेंट पॉइंटर मेमोरी में कहां बैठता है? मैं जानता हूं कि स्मृति के वैश्विक हिस्से में सभी कास्ट बैठते हैं, लेकिन मैं एक कास्ट के बारे में 100% निश्चित नहीं हूं कि आप एक मजेदार संस्करण के रूप में पास हो जाते हैं। धन्यवाद और खेद है अगर यह विषय है
तोमर

77

मैं केवल const तर्कों का उपयोग करने का एक बिंदु बनाता हूं क्योंकि यह अधिक संकलक जांच को सक्षम करता है: यदि मैं गलती से फ़ंक्शन के अंदर एक तर्क मान देता हूं, तो संकलक मुझे काटता है।

मैं शायद ही कभी चर का पुन: उपयोग करता हूं, नए मूल्यों को रखने के लिए नए चर बनाने के लिए यह क्लीनर है, इसलिए अनिवार्य रूप से मेरी सभी चर घोषणाएं हैं const(कुछ मामलों को छोड़कर जैसे लूप चर जहां constकाम करने से कोड को रोकेंगे)।

ध्यान दें कि यह फ़ंक्शन की परिभाषा में केवल समझ में आता है । यह घोषणा में शामिल नहीं है , जो कि उपयोगकर्ता देखता है। और उपयोगकर्ता परवाह नहीं करता है कि मैं constफ़ंक्शन के अंदर मापदंडों के लिए उपयोग करता हूं या नहीं ।

उदाहरण:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

ध्यान दें कि तर्क और स्थानीय चर दोनों कैसे हैं const। न तो आवश्यक है, लेकिन उन कार्यों के साथ जो थोड़ा और भी बड़े हैं, इसने मुझे बार-बार गलतियां करने से बचाया है।


17
+1दूसरे से constकट्टरपंथी। हालांकि, मैं अपने कंपाइलरों को मुझ पर भौंकना पसंद करता हूं। मैं बहुत अधिक त्रुटियां करता हूं और वे बुरी तरह से पीड़ित होते हैं।
sbi

2
+1, मैं " constजब तक कोई अच्छा कारण नहीं है" रणनीति की अत्यधिक अनुशंसा करता हूं । वहाँ कुछ अच्छा अपवाद हालांकि, उदा कॉपी और स्वैप
Flexo

2
अगर मैं एक नई भाषा डिज़ाइन कर रहा था, तो घोषित ऑब्जेक्ट्स केवल-पढ़ने के लिए ("कॉन्स्ट") डिफ़ॉल्ट रूप से होंगे। किसी वस्तु को योग्य बनाने के लिए आपको कुछ विशेष वाक्यविन्यास, शायद "var" कीवर्ड की आवश्यकता होगी।
कीथ थॉम्पसन

@ के साथ मुझे "निश्चित रूप से" कहने का मोह है। इस बिंदु पर बाकी सब कुछ मूर्खतापूर्ण है। लेकिन दुर्भाग्य से अधिकांश भाषा डिजाइनर हमसे असहमत दिखते हैं ... वास्तव में, मैं पहले से ही बहुत अधिक (अब हटाए गए प्रश्न में, "आपकी सबसे विवादास्पद प्रोग्रामिंग राय क्या है?") पोस्ट कर चुका हूं।
कोनराड रुडोल्फ

1
@ कुबाओबर मैं नहीं देखता कि यह सब पर एक निराशा है। यदि आपको बाद में पता चलता है कि आपको फ़ंक्शन के निकाय के भीतर जो कुछ भी पारित किया गया था, उसे संशोधित करने की आवश्यकता है, तो बस constतर्क से छोड़ दें , और अधिमानतः टिप्पणी क्यों करें। उस छोटे से अतिरिक्त काम में सभी तर्कों को गैर- constडिफ़ॉल्ट रूप से चिह्नित करने और सभी संभावित त्रुटियों को बनाने के लिए खुद को खोलने का औचित्य नहीं है।
अंडरस्कोर_ड

20

आपका प्रश्न कुछ और सामान्य पर छूता है: क्या फ़ंक्शन तर्कों को संकुचित होना चाहिए?

मूल्य तर्कों की स्थिरता (आपके सूचक की तरह) एक कार्यान्वयन विवरण है , और यह फ़ंक्शन घोषणा का हिस्सा नहीं बनता है। इसका मतलब है कि आपका कार्य हमेशा यही है:

void foo(T);

यह पूरी तरह से फ़ंक्शन के कार्यान्वयनकर्ता पर निर्भर है कि वह फ़ंक्शंस-स्कोप तर्क चर का उपयोग किसी परिवर्तनशील या स्थिर तरीके से करना चाहता है या नहीं:

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

तो, constघोषणा (हेडर) में कभी नहीं डालने के लिए सरल नियम का पालन करें , और यदि आप चर को संशोधित करना चाहते हैं या नहीं करना चाहते हैं, तो इसे परिभाषा (कार्यान्वयन) में डालें।


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

@KeithThompson: यदि आप नहीं चाहते हैं तो ठीक है, आपको यह करने की जरूरत नहीं है। बस constघोषणा में मत डालो । यह पूरी तरह से आप पर निर्भर है यदि आप कार्यान्वयन में क्वालीफायर जोड़ना चाहते हैं।
केरेक एसबी

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

16

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


मुझे नहीं पता था। इसलिए अगर मैं शून्य फू (int * t ptr) के साथ void foo (int * const ptr) को ओवरलोड करने की कोशिश करता हूं तो मुझे कंपाइलर एरर मिलेगा। धन्यवाद!
आर रुइज

@ R.Ruiz। यदि आपकी मंदी शून्य फू (int * t ptr) है और आपकी परिभाषा शून्य foo (int * const ptr) है, तो आपको संकलक त्रुटि नहीं मिलेगी।
ट्रेवर हिक्की

1
@TrevorHickey वे ... लेकिन उस कारण के लिए नहीं जो वे सोचते हैं। int* t ptrएक वाक्यविन्यास त्रुटि है। इसके बिना, दोनों ओवरलोडिंग उद्देश्यों के लिए समान हैं।
अंडरस्कोर_ड

14

आप सही हैं, फोन करने वाले के लिए यह बिल्कुल कोई फर्क नहीं पड़ता। लेकिन फ़ंक्शन के लेखक के लिए यह एक सुरक्षा जाल हो सकता है "ठीक है, मुझे यह सुनिश्चित करने की आवश्यकता है कि मैं इस बात को गलत नहीं बनाता"। बहुत उपयोगी नहीं है, लेकिन बेकार भी नहीं है।

यह मूल रूप int const the_answer = 42से आपके कार्यक्रम में होने के समान है ।


1
इससे कोई फर्क नहीं पड़ता कि वे इसे कहां इंगित करते हैं, यह स्टैक पर आवंटित एक सूचक है? फ़ंक्शन गड़बड़ नहीं कर सकता। यह पॉइंटर-टू-पॉइंटर्स से निपटने के दौरान रीड-ओनली पॉइंटर्स का उपयोग करने के लिए कहीं अधिक समझ में आता है। यह int const के रूप में एक ही बात नहीं है ! मैं इसे केवल उस भ्रामक कथन के लिए नीचा दिखाऊंगा। const intऔर int constसमतुल्य हैं, जबकि const int*और int* constदो अलग-अलग अर्थ हैं!
लंडिन

1
@ लंडिन मान लीजिए कि फ़ंक्शन बड़ा है और इसमें अन्य सामान है। अकस्मात कोई इसे कुछ और (फ़ंक्शन के ढेर पर) इंगित कर सकता है। यह फोन करने वाले के लिए समस्याग्रस्त नहीं है लेकिन निश्चित रूप से कैली के लिए हो सकता है। मुझे अपने बयानों में कुछ भी भ्रामक नहीं दिखता है: " फ़ंक्शन के लेखक के लिए यह एक सुरक्षा जाल हो सकता है"।
cnicutar

1
@ लुंडिन int constभाग के बारे में; मैं कुछ समय के लिए कास्ट (यह अप्राकृतिक लगता है) से पहले टाइप कर रहा हूं और मुझे मतभेदों के बारे में पता है। यह लेख उपयोगी साबित हो सकता है। हालाँकि इस शैली पर स्विच करने के लिए मेरे पास कुछ अलग कारण थे।
cnicutar

1
ठीक है। मैंने अपने मामले में किसी और के बारे में सिर्फ एक लंबा-चौड़ा जवाब लिखा है, लेकिन आप और मैं इस पोस्ट को पढ़ रहे हैं। मैंने एक तर्क भी शामिल किया कि इंट कास्ट खराब शैली क्यों है और कॉन्स्ट इंट अच्छी शैली है।
लुंडिन

लुंडिन, मैं भी पढ़ रहा हूँ! मैं मानता हूं कि कॉन्स्ट इंट बेहतर शैली है। @cnicutar, लुंडिन के लिए आपकी पहली प्रतिक्रिया वह है जिसे मैं खोज रहा था जब मैंने यह प्रश्न प्रस्तुत किया था। तो समस्या फोन करने वाले में नहीं है (जैसा कि मैंने बिना किसी विचार के), लेकिन कास्ट कैली के लिए एक आश्वासन है। मुझे यह विचार पसंद आया, धन्यवाद।
आर रुइज

14

constकीवर्ड के लिए बहुत कुछ है , यह एक जटिल है। आम तौर पर, अपने प्रोग्राम में बहुत सी कास्ट को जोड़ना अच्छा प्रोग्रामिंग प्रैक्टिस माना जाता है, "कांस्ट शुद्धता" के लिए वेब पर खोज करें और आपको इसके बारे में बहुत सारी जानकारी मिल जाएगी।

कॉन्स्ट कीवर्ड एक तथाकथित "टाइप क्वालिफायर" है, अन्य हैं volatileऔर restrict। कम से कम अस्थिर कांस्टेबल के समान (भ्रमित) नियमों का पालन करता है।


सबसे पहले, कास्ट कीवर्ड दो उद्देश्यों को पूरा करता है। सबसे स्पष्ट एक जानबूझकर या आकस्मिक दुरुपयोग से डेटा (और संकेत) को केवल पढ़ने के लिए बनाने से बचाने के लिए है। एक संकलित चर को संशोधित करने का कोई भी प्रयास कंपाइलर द्वारा संकलन-समय पर देखा जाएगा।

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

constकीवर्ड का उपयोग करने के कई अलग-अलग तरीके हैं :

एक स्थिर चर घोषित करें।

यह या तो किया जा सकता है

const int X=1; or
int const X=1;

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

दूसरी पंक्ति को खराब शैली का कारण क्यों माना जाता है, शायद इसलिए कि "स्टोरेज-क्लास स्पेसियर्स" जैसे कि स्थिर और बाहरी भी वास्तविक प्रकार के बाद घोषित किए जा सकते हैं , int staticआदि। लेकिन स्टोरेज-क्लास स्पेसर्स के लिए ऐसा करना एक अप्रचलित विशेषता के रूप में लेबल किया जाता है। सी कमेटी (आईएसओ 9899 एन 1539 ड्राफ्ट, 6.11.5) द्वारा। इसलिए, संगति के लिए किसी को उस तरह से टाइप क्वालिफायर नहीं लिखना चाहिए। यह पाठक को किसी भी तरह भ्रमित करने के लिए कोई अन्य उद्देश्य नहीं देता है।

एक स्थिर चर के लिए एक सूचक घोषित करें।

const int* ptr = &X;

इसका मतलब है कि 'X' की सामग्री को संशोधित नहीं किया जा सकता है। यह सामान्य तरीका है जो आप बिंदुओं को इस तरह से घोषित करते हैं, मुख्य रूप से "कांस्ट शुद्धता" के लिए फ़ंक्शन मापदंडों का हिस्सा है। क्योंकि 'X' को वास्तव में कॉन्स्टेबल के रूप में घोषित नहीं किया जाना है, यह कोई भी परिवर्तनशील हो सकता है। दूसरे शब्दों में, आप हमेशा एक चर को "अपग्रेड" कर सकते हैं। तकनीकी रूप से, C स्पष्ट टाइपकास्ट द्वारा एक सादे चर में const से डाउनग्रेड करने की अनुमति देता है, लेकिन ऐसा करना बुरा प्रोग्रामिंग माना जाता है और संकलक आमतौर पर इसके खिलाफ चेतावनी देते हैं।

एक स्थिर सूचक की घोषणा करें

int* const ptr = &X;

इसका मतलब है कि सूचक स्वयं स्थिर है। आप यह इंगित कर सकते हैं कि यह किस बिंदु पर है, लेकिन आप सूचक को स्वयं संशोधित नहीं कर सकते। इसके कई उपयोग नहीं हैं, कुछ हैं, जैसे कि यह सुनिश्चित करने के लिए कि एक संकेतक-पॉइंट-एट (पॉइंटर-टू-पॉइंटर) के पास यह नहीं है कि फ़ंक्शन के पैरामीटर के रूप में पास करते समय इसका पता बदल गया है। आपको कुछ इस तरह से पढ़ने योग्य नहीं लिखना होगा:

void func (int*const* ptrptr)

मुझे संदेह है कि कई सी प्रोग्रामर को कॉन्स्टेबल और * वहीं मिल सकते हैं। मुझे पता है कि मैं नहीं कर सकता - मुझे जीसीसी के साथ जांच करनी थी। मुझे लगता है कि आप शायद ही कभी सूचक-टू-पॉइंटर के लिए उस वाक्यविन्यास को देखते हैं, भले ही इसे अच्छा प्रोग्रामिंग अभ्यास माना जाता है।

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

और निश्चित रूप से, जैसा कि अन्य उत्तरों से संकेत मिलता है, निरंतर पॉइंटर्स का उपयोग "कांस्ट शुद्धता" को लागू करने के लिए भी किया जा सकता है।

निरंतर डेटा के लिए एक निरंतर सूचक की घोषणा करें

const int* const ptr=&X;

यह ऊपर वर्णित दो सूचक प्रकार हैं, दोनों की सभी विशेषताओं के साथ।

केवल-पढ़ने के लिए सदस्य फ़ंक्शन (C ++) की घोषणा करें

चूंकि यह C ++ टैग किया गया है, इसलिए मुझे यह भी उल्लेख करना चाहिए कि आप एक वर्ग के सदस्य कार्यों को कास्ट घोषित कर सकते हैं। इसका मतलब यह है कि फ़ंक्शन को कक्षा के किसी अन्य सदस्य को कॉल करने की अनुमति नहीं दी जाती है, जिसे दोनों कक्षा के प्रोग्रामर को आकस्मिक त्रुटियों से बचाता है, लेकिन सदस्य फ़ंक्शन के कॉलर को सूचित करता है कि वे कुछ भी गड़बड़ नहीं करेंगे इसे फोन करके। वाक्य रचना है:

void MyClass::func (void) const;

8

... आज मुझे एहसास हुआ कि संकेत कार्यों के लिए मूल्य से पारित किए जाते हैं, जो समझ में आता है।

(imo) यह वास्तव में डिफ़ॉल्ट के रूप में कोई मतलब नहीं है। अधिक समझदार डिफ़ॉल्ट को गैर-पुन: संकेत सूचक के रूप में पारित करना है (int* const arg ) के । यही कारण है कि, मैंने तर्क दिया है कि संकेत के रूप में पारित किए गए तर्क को स्पष्ट रूप से कास्ट घोषित किया गया था।

तो कब्ज का फायदा क्या है?

लाभ यह है कि यह काफी आसान है और कभी-कभी अस्पष्ट हो जाता है जब आप पते को संशोधित करते हैं तो तर्क बिंदुओं को संशोधित करता है, जैसे कि आप बग को पेश कर सकते हैं जब यह आसानी से स्थिर नहीं होता है। पते में परिवर्तन करना atypical है। यदि आपका इरादा पते को संशोधित करने का है, तो स्थानीय चर बनाना स्पष्ट है। के रूप में अच्छी तरह से, कच्चे सूचक हेरफेर कीड़े को पेश करने का एक आसान तरीका है।

इसलिए जब आप पते को बदलना चाहते हैं तो अपरिवर्तनीय पते से गुजरना और प्रतिलिपि बनाना (उन असामान्य मामलों में) स्पष्ट है:

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

पठनीयता में सुधार करते हुए, उस स्थानीय को जोड़ना वास्तव में स्वतंत्र है, और यह त्रुटियों के लिए मौका कम कर देता है।

एक उच्च स्तर पर: यदि आप तर्क को एक सरणी के रूप में जोड़-तोड़ कर रहे हैं, तो यह आमतौर पर ग्राहक को कंटेनर / संग्रह के रूप में तर्क को घोषित करने के लिए स्पष्ट और कम त्रुटि वाला होता है।

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


4
इसके लिए +1 डिफ़ॉल्ट है। C ++, अधिकांश भाषाओं की तरह, यह गलत तरीका है। एक constकीवर्ड होने के बजाय , इसमें एक कीवर्ड होना चाहिए mutable(ठीक है, यह है, लेकिन गलत शब्दार्थ के साथ)।
कोनराड रुडोल्फ

2
दिलचस्प विचार डिफ़ॉल्ट के रूप में कास्ट है। आपके उत्तर के लिए धन्यवाद!
आर रुइज

6

यदि आप एम्बेडेड सिस्टम या डिवाइस ड्राइवर प्रोग्रामिंग करते हैं, जहां आपके पास मेमोरी मैप्ड डिवाइस हैं, तो 'कॉन्स्ट' के दोनों रूप अक्सर उपयोग किए जाते हैं, एक पॉइंटर को फिर से असाइन किए जाने से रोकने के लिए (क्योंकि यह एक निश्चित हार्डवेयर पते पर इंगित करता है।) और, यदि परिधीय। इसे पंजीकृत करना एक रीड-ओनली हार्डवेयर रजिस्टर है, फिर एक अन्य कास्ट रनटाइम के बजाय संकलन समय पर बहुत सारी त्रुटियों का पता लगाएगा।

एक केवल पढ़ने के लिए 16 बिट परिधीय चिप रजिस्टर कुछ इस तरह लग सकता है:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

फिर आप असेंबली भाषा का सहारा लिए बिना आसानी से हार्डवेयर रजिस्टर पढ़ सकते हैं:

input_word = *peripheral;


5

int iVal = 10; int * const ipPtr = & iVal;

एक सामान्य कॉन्स्टेबल वैरिएबल की तरह, एक कांस्टी पॉइंटर को घोषणा पर एक मूल्य के लिए आरंभीकृत किया जाना चाहिए, और इसका मान नहीं बदला जा सकता है।

इसका मतलब यह है कि एक कास्ट पॉइंटर हमेशा एक ही मान को इंगित करेगा। उपरोक्त मामले में, ipPtr हमेशा iVal के पते को इंगित करेगा। हालाँकि, क्योंकि इंगित किया जा रहा मूल्य अभी भी गैर-कास्ट है, पॉइंटर को डीफ्रॉन्फ्रेंसिंग के माध्यम से इंगित किए जा रहे मूल्य को बदलना संभव है:

* ipPtr = 6; // अनुमति दी है, क्योंकि pnPtr एक गैर-कॉन्स्टेंस इंट को इंगित करता है


5

किसी भी अन्य प्रकार के बारे में एक ही सवाल पूछा जा सकता है (न केवल संकेत):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}

LOL, आप सही हैं। पॉइंटर्स का मामला सबसे पहले मेरे दिमाग में आया क्योंकि मुझे लगा कि वे विशेष हैं, लेकिन वे किसी अन्य वैरिएबल की तरह हैं जो मूल्य से गुजरते हैं।
आर रुइज

5

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

आपके विशेष मामले में, कार्यात्मक रूप से यह कई अन्य मामलों की तरह अंतर नहीं करता है जब आप स्थानीय चर को कास्ट घोषित करते हैं लेकिन यह एक प्रतिबंध लगाता है कि आप इस चर को संशोधित नहीं कर सकते।


आपकी टिप्पणी से यह पता चलता है कि मेरा प्रश्न कितना सही है क्योंकि आप सही हैं: यह एक ही बात है क्योंकि किसी भी अन्य पैरामीटर के रूप में कास्ट है। यह बेकार लग सकता है लेकिन प्रोग्रामर को यह प्रतिबंध लगाने और बग से बचने में मदद करने के लिए है
आर रुइज़।

4

किसी फ़ंक्शन के लिए एक कॉस्ट पॉइंटर पास करना बहुत कम समझ में आता है, क्योंकि इसे वैसे भी मान दिया जाएगा। यह उन चीजों में से एक है जिन्हें सामान्य भाषा डिजाइन द्वारा अनुमति दी जाती है। इसे सिर्फ इसलिए रोक देना क्योंकि इसका कोई मतलब नहीं है कि यह सिर्फ भाषा को युक्ति बना देगा। बड़ा।

यदि आप किसी फ़ंक्शन के अंदर हैं, तो यह निश्चित रूप से एक और मामला है। एक पॉइंटर होने से जो यह इंगित नहीं कर सकता है कि यह एक जोर है जो कोड को स्पष्ट करता है।


4

मुझे लगता है कि एक फायदा यह होगा कि कंपाइलर फ़ंक्शन के अंदर अधिक आक्रामक अनुकूलन कर सकता है, यह जानते हुए कि यह पॉइंटर नहीं बदल सकता है।

यह उदा से भी बचता है। इस पॉइंटर को एक सबफ़ंक्शन में पास करना जो एक नॉन-कास्ट पॉइंटर रेफ़रेंस को स्वीकार करता है (और इसलिए पॉइंटर को बदल सकता है जैसे void f(int *&p)), लेकिन मैं मानता हूं, इस मामले में उपयोगिता कुछ हद तक सीमित है।


अनुकूलन के लिए +1। कम से कम किताबें कहती हैं कि यह सच है। मैं यह समझना चाहूंगा कि वास्तव में उन अनुकूलन क्या हैं
आर रुइज़।

4

एक कास्ट पॉइंटर अत्यधिक लागू होने का एक उदाहरण इस प्रकार प्रदर्शित किया जा सकता है। विचार करें कि आपके पास एक गतिशील सरणी के साथ एक वर्ग है, और आप उपयोगकर्ता को सरणी तक पहुंच देना चाहते हैं, लेकिन उन्हें सूचक को बदलने के अधिकार दिए बिना। विचार करें:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

जो पैदा करता है:

इनपुट डेटा
डाल डेटा

लेकिन अगर हम यह कोशिश करें:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

हमें मिला:

त्रुटि: असाइनमेंट के बाएं ऑपरेंड के रूप में आवश्यक अंतराल // Drat फिर से नाकाम!

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

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

हम अभी भी पॉइंटर की मेमोरी रेफरेंस को डिलीट कर सकते हैं, भले ही हम पॉइंटर को खुद ही संशोधित नहीं कर सकते।

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

संपादित करें:

GetArray के कारण असाइन नहीं होने की टिप्पणी okorz001 नोट करने के बाद () एक राइट-वैल्यू ऑपरेंड होने के कारण, उनकी टिप्पणी पूरी तरह से सही है, लेकिन उपरोक्त अभी भी लागू होता है यदि आप पॉइंटर का एक संदर्भ वापस करना चाहते थे (मुझे लगता है कि GetArray था उदाहरण के लिए)

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

पहले एक त्रुटि के परिणामस्वरूप वापस आएगा:

त्रुटि: केवल-पढ़ने के लिए स्थान 'Temp.TestA :: GetArray ()'

लेकिन दूसरा नीचे की ओर संभावित परिणामों के बावजूद पूरी तरह से घटित होगा।

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

विचार करें कि क्या हमारे पास एक फ़ंक्शन है जो एक कॉन्स्टेंट पॉइंटर लेता है (बनाम जो ऐसा नहीं करता है):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

इस प्रकार संदेश में त्रुटि का कारण बनता है:

त्रुटि: रीड-ओनली पैरामीटर 'डेटा' का घटाव

यह अच्छा है क्योंकि हम शायद ऐसा नहीं करना चाहते हैं, जब तक कि हम टिप्पणियों में बताई गई समस्याओं का कारण नहीं बनना चाहते। अगर हम कास्ट फंक्शन में कमी को संपादित करते हैं, तो निम्न होते हैं:

नॉनकॉन्स्ट: ए कांस्ट:
बी

स्पष्ट रूप से, भले ही ए 'डेटा [1]' है, इसे 'डेटा [0]' के रूप में माना जा रहा है, क्योंकि नॉनकॉस्ट पॉइंटर ने वेतन वृद्धि के संचालन की अनुमति दी थी। लागू किए गए कॉन्स्ट के साथ, जैसा कि एक अन्य व्यक्ति लिखता है, हम संभावित बग को होने से पहले पकड़ लेते हैं।

एक अन्य मुख्य विचार, यह है कि एक कास्ट पॉइंटर को एक छद्म संदर्भ के रूप में इस्तेमाल किया जा सकता है, इस बात में कि संदर्भ बिंदुओं को बदला नहीं जा सकता है (एक चमत्कार, यदि शायद यह कैसे लागू किया गया था)। विचार करें:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

संकलन करने का प्रयास करते समय, निम्न त्रुटि उत्पन्न करता है:

त्रुटि: केवल-पढ़ने के लिए परिवर्तनशील 'B'

यदि ए को एक निरंतर संदर्भ चाहिए था तो यह संभवतः एक बुरी बात है। यदि B = NULLटिप्पणी की जाती है, तो संकलक हमें खुशी से संशोधित करेगा *Bऔर इसलिए ए। यह ints के साथ उपयोगी नहीं लग सकता है, लेकिन विचार करें कि क्या आपके पास ग्राफ़िकल एप्लिकेशन का एक भी रुख है जहां आप एक अपरिवर्तनीय पॉइंटर चाहते थे जो इसे संदर्भित करता है - जिसे आप पास कर सकते हैं चारों ओर।

यह उपयोग परिवर्तनशील है (अनजाने वाक्य को बहाना), लेकिन सही ढंग से उपयोग किया जाता है, यह प्रोग्रामिंग के साथ सहायता करने के लिए बॉक्स में एक और उपकरण है।


2
Temp.GetArray() = NULLविफल रहता है क्योंकि Temp.GetArray()एक प्रतिद्वंद्विता है, इसलिए नहीं कि यह कब्ज-योग्य है। इसके अतिरिक्त, मेरा मानना ​​है कि सभी रिटर्न प्रकारों से कॉन्स्ट-क्वालिफायर छीन लिया गया है।
ऑस्कर कोरज़

@ okorz001: परीक्षण के बाद, आप वास्तव में सही हैं। हालाँकि, यदि आप सूचक पर ही संदर्भ लौटाते हैं तो उपरोक्त लागू होता है। मैं अपने पोस्ट को अपने अनुसार संपादित करूंगा।
SSight3

3

ऐसे पॉइंटर्स के बारे में कुछ खास नहीं है जहाँ आप कभी नहीं चाहेंगे कि वे कांस्टेबल हों। जैसे आप कक्षा के सदस्य को निरंतर intमान दे सकते हैं, वैसे ही समान कारणों से भी आपके पास निरंतर संकेत हो सकते हैं: आप यह सुनिश्चित करना चाहते हैं कि कोई भी कभी भी नहीं बदलता है जो इंगित किया जा रहा है। C ++ कुछ हद तक इसे संदर्भित करता है, लेकिन सूचक व्यवहार C से विरासत में मिला है।


3

मेरा मानना ​​है कि इससे कोड को फ़ंक्शन बॉडी के भीतर पॉइंटर को बढ़ाने या घटाने से रोका जा सकेगा।


3

किसी भी चर को घोषित करने के प्रकार जैसे-
(1) स्थिर चर घोषित करना।
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) एक पॉइंटर को एक स्थिर वैरिएबल पर घोषित करें
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified


जब हम इसके वास्तविक मान को बदलना नहीं चाहते हैं, तो हम एक तर्क के रूप में एक फ़ंक्शन के रूप में एक पॉइंटर वैरिएबल प्रदान करते हैं
Pyadav
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.