C ++ में चर कैसे अपने प्रकार को संग्रहीत करते हैं?


42

यदि मैं एक निश्चित प्रकार के एक चर को परिभाषित करता हूं (जो, जहां तक ​​मुझे पता है, बस चर की सामग्री के लिए डेटा आवंटित करता है), यह किस प्रकार के चर का ट्रैक रखता है?


8
" यह कैसे ट्रैक रखता है " में "आप" द्वारा किसका / क्या जिक्र कर रहे हैं ? संकलक या सीपीयू या कुछ और / जैसे भाषा या कार्यक्रम?
एरिक Eidt


8
@ErikEidt IMO ओपी का स्पष्ट अर्थ है कि "वैरिएबल ओन" "इट"। बेशक सवाल का दो-शब्द का जवाब है "यह नहीं है"।
एलेफ़ेज़ेरो

2
बड़ा सवाल! विशेष रूप से प्रासंगिक आज सभी फैंसी भाषाओं को दिया गया है जो अपने प्रकार को संग्रहीत करते हैं।
ट्रेवर बॉयड स्मिथ

@alephzero यह स्पष्ट रूप से एक प्रमुख सवाल था।
लुआण

जवाबों:


105

चर (या अधिक सामान्यतः: C के अर्थ में "ऑब्जेक्ट") रनटाइम पर अपने प्रकार को संग्रहीत नहीं करते हैं। जहां तक ​​मशीन कोड का सवाल है, केवल अनपेड मेमोरी है। इसके बजाय, इस डेटा के संचालन डेटा को एक विशिष्ट प्रकार (जैसे एक फ्लोट के रूप में या एक पॉइंटर के रूप में) की व्याख्या करते हैं। प्रकार केवल संकलक द्वारा उपयोग किए जाते हैं।

उदाहरण के लिए, हमारे पास एक संरचना या वर्ग struct Foo { int x; float y; };और एक चर हो सकता है Foo f {}। फ़ील्ड एक्सेस कैसे auto result = f.y;संकलित किया जा सकता है? संकलक जानता है कि fएक प्रकार की वस्तु है Fooऔर Foo-objects के लेआउट को जानता है । प्लेटफ़ॉर्म-विशिष्ट विवरणों के आधार पर, इसे "स्टार्टर के पॉइंटर को ले लो f, 4 बाइट्स जोड़ने, फिर 4 बाइट्स लोड करने और इस डेटा को फ्लोट के रूप में व्याख्या करने के लिए संकलित किया जा सकता है ।" ) फ़्लोटिंग या इन्ट्स लोड करने के लिए अलग-अलग प्रोसेसर निर्देश हैं।

एक उदाहरण जहां C ++ टाइप सिस्टम हमारे लिए टाइप का ट्रैक नहीं रख सकता है वह एक यूनियन जैसा है union Bar { int as_int; float as_float; }। एक संघ में विभिन्न प्रकार की एक वस्तु तक होती है। यदि हम किसी वस्तु को संघ में संग्रहीत करते हैं, तो यह संघ का सक्रिय प्रकार है। हमें केवल उस प्रकार को संघ से बाहर निकालने की कोशिश करनी चाहिए, और कुछ भी अपरिभाषित व्यवहार नहीं होगा। या तो हम "जानते हैं" प्रोग्रामिंग करते समय कि सक्रिय प्रकार क्या है, या हम एक टैग किए गए संघ बना सकते हैं जहां हम एक प्रकार का टैग (आमतौर पर एक एनम) अलग से संग्रहीत करते हैं। यह C में एक सामान्य तकनीक है, लेकिन क्योंकि हमें संघ को रखना है और सिंक में टाइप टैग यह काफी त्रुटि प्रवण है। एक void*पॉइंटर एक संघ के समान होता है लेकिन केवल पॉइंटर ऑब्जेक्ट्स को पकड़ सकता है, सिवाय फ़ंक्शन पॉइंटर्स के।
C ++ अज्ञात प्रकार की वस्तुओं से निपटने के लिए दो बेहतर तंत्र प्रदान करता है: प्रकार के क्षरण को करने के लिए हम ऑब्जेक्ट-ओरिएंटेड तकनीकों का उपयोग कर सकते हैं (केवल वर्चुअल विधियों के माध्यम से ऑब्जेक्ट के साथ इंटरैक्ट करते हैं ताकि हमें वास्तविक प्रकार जानने की आवश्यकता न हो), या हम कर सकते हैं का उपयोग करें std::variant, एक प्रकार का सुरक्षित संघ।

एक मामला है जहां C ++ ऑब्जेक्ट के प्रकार को संग्रहीत करता है: यदि ऑब्जेक्ट के वर्ग में कोई वर्चुअल तरीके ("पॉलीमॉर्फिक प्रकार", उर्फ ​​इंटरफ़ेस) है। वर्चुअल मेथड कॉल का लक्ष्य संकलन समय पर अज्ञात है और इसे गतिशील प्रकार के ऑब्जेक्ट ("डायनेमिक डिस्पैच") के आधार पर रन टाइम पर हल किया जाता है। अधिकांश कंपाइलर ऑब्जेक्ट के शुरू में एक वर्चुअल फंक्शन टेबल ("वॉयटेबल") स्टोर करके इसे लागू करते हैं। रन टाइम पर ऑब्जेक्ट के प्रकार को प्राप्त करने के लिए विटेबल का भी उपयोग किया जा सकता है। इसके बाद हम संकलित-स्थिर ज्ञात अभिव्यक्ति के प्रकार और रनटाइम पर किसी ऑब्जेक्ट के गतिशील प्रकार के बीच अंतर कर सकते हैं।

C ++ हमें typeid()ऑपरेटर के साथ ऑब्जेक्ट के गतिशील प्रकार का निरीक्षण करने की अनुमति देता है जो हमें एक std::type_infoऑब्जेक्ट देता है । या तो संकलक को संकलन के समय वस्तु का प्रकार पता है, या संकलक ने वस्तु के अंदर आवश्यक प्रकार की जानकारी संग्रहीत की है और इसे रनटाइम पर पुनः प्राप्त कर सकता है।


3
बहुत व्यापक है।
डेडुप्लिकेटर

9
ध्यान दें कि पॉलीमॉर्फिक ऑब्जेक्ट के प्रकार को एक्सेस करने के लिए कंपाइलर को अभी भी पता होना चाहिए कि ऑब्जेक्ट एक विशेष वंशानुक्रम परिवार से संबंधित है (अर्थात ऑब्जेक्ट के लिए टाइप किया गया संदर्भ / सूचक है, नहीं void*)।
रुस्लान

5
+0 क्योंकि पहला वाक्य पिछले दो पैराग्राफों को असत्य है, इसे सही करें।
मार्सिन

3
आम तौर पर एक पॉलीमॉर्फिक ऑब्जेक्ट की शुरुआत में जो संग्रहीत किया जाता है वह वर्चुअल विधि तालिका के लिए एक संकेतक है, न कि तालिका।
पीटर ग्रीन

3
@ v.oddou अपने पैराग्राफ में मैंने कुछ विवरणों को अनदेखा किया। typeid(e)अभिव्यक्ति के स्थिर प्रकार का परिचय देता है e। यदि स्थैतिक प्रकार एक बहुरूपी प्रकार है, तो अभिव्यक्ति का मूल्यांकन किया जाएगा और उस वस्तु के गतिशील प्रकार को पुनः प्राप्त किया जाएगा। आप अज्ञात प्रकार की स्मृति में टाइपिड को इंगित नहीं कर सकते हैं और उपयोगी जानकारी प्राप्त कर सकते हैं। जैसे एक संघ का प्रकार संघ का वर्णन करता है, न कि संघ की वस्तु। एक void*का प्रकार केवल एक शून्य सूचक है। और void*इसकी सामग्री को प्राप्त करना संभव नहीं है । C ++ में कोई बॉक्सिंग नहीं है जब तक कि स्पष्ट रूप से उस तरह से प्रोग्राम न किया गया हो।
आमोन

51

अन्य उत्तर अच्छी तरह से तकनीकी पहलू बताते हैं, लेकिन मैं कुछ सामान्य "मशीन कोड के बारे में कैसे सोचूं" जोड़ना चाहूंगा।

संकलन के बाद मशीन कोड बहुत गूंगा है, और यह वास्तव में सिर्फ यह मानता है कि सब कुछ उद्देश्य के अनुसार काम करता है। कहते हैं कि आपके पास एक सरल कार्य है

bool isEven(int i) { return i % 2 == 0; }

यह एक इंट लेता है, और एक बूल को बाहर निकालता है।

इसे संकलित करने के बाद, आप इसके बारे में इस स्वचालित संतरे के जूसर की तरह सोच सकते हैं:

स्वचालित संतरे का जूसर

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

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

चेतावनी यह है कि बीमार कोड के कुछ मामले हैं जो संकलक पास करेंगे। उदाहरण हैं:

  • गलत प्रकार कास्टिंग: स्पष्ट डाले सही माना जाता है, और यह सुनिश्चित करने के लिए कि वह कास्टिंग नहीं है प्रोग्रामर पर है void*करने के लिए orange*जब वहाँ सूचक के दूसरे छोर पर एक सेब है,
  • स्मृति प्रबंधन के मुद्दे जैसे अशक्त संकेत, झूलने वाले बिंदु या उपयोग-बाद-गुंजाइश; संकलक उनमें से अधिकांश को खोजने में सक्षम नहीं है,
  • मुझे यकीन है कि मुझे कुछ और याद आ रहा है।

जैसा कि कहा गया है, संकलित कोड जूसर मशीन की तरह है - यह नहीं जानता कि यह क्या प्रक्रिया करता है, यह सिर्फ निर्देशों को निष्पादित करता है। और अगर निर्देश गलत हैं, तो यह टूट जाता है। यही कारण है कि C ++ में उपरोक्त समस्याओं के परिणामस्वरूप अनियंत्रित क्रैश होते हैं।


4
संकलक यह जांचने का प्रयास करता है कि फ़ंक्शन सही प्रकार का एक ऑब्जेक्ट है, लेकिन सी और सी ++ दोनों कंपाइलर के लिए हर मामले में इसे साबित करने के लिए बहुत जटिल हैं। तो, अपने सेब और संतरे juicer की तुलना में काफी शिक्षाप्रद है।
कलकंस

@ कमल आपकी टिप्पणी के लिए धन्यवाद! यह वाक्य वास्तव में एक निरीक्षण था। मैंने संभावित समस्याओं पर थोड़ा विस्तार किया, वे वास्तव में प्रश्न से संबंधित हैं।
फ्रैक्स

5
मशीन कोड के लिए महान रूपक वाह! आपके रूपक को चित्र द्वारा 10x बेहतर बनाया जाता है!
ट्रेवर बॉयड स्मिथ

2
"मुझे यकीन है कि कुछ और है जो मुझे याद आ रहा है।" - बेशक! सी void*के लिए coerces foo*, हमेशा की तरह गणित प्रोन्नति, unionप्रकार punning, NULLबनाम nullptr, यहां तक कि बस होने एक बुरा सूचक यूबी, आदि है लेकिन मैं वास्तव में अपने जवाब में सुधार होगा उन चीजों बाहर के सभी लिस्टिंग नहीं लगता कि, तो यह शायद छुट्टी के लिए सबसे अच्छा है जैसा है वैसा है।
केविन

@ केविन मुझे नहीं लगता कि यहां सी जोड़ना आवश्यक है, क्योंकि प्रश्न केवल सी ++ के रूप में टैग किया गया है। और C ++ में void*स्पष्ट रूप से परिवर्तित नहीं होता है foo*, और unionटाइपिंग का समर्थन समर्थित नहीं है (UB है)।
रुस्लान

3

एक चर में C जैसी भाषा में कई मूलभूत गुण होते हैं:

  1. एक नाम
  2. एक प्रकार
  3. एक स्कोप
  4. एक जीवनकाल
  5. एक स्थल
  6. एक कीमत

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

केवल कुछ हद तक सरल करना, एक बार जब आपके प्रोग्राम को संकलक द्वारा मशीन कोड में अनुवादित किया जाता है, तो स्थान (5), कुछ मेमोरी या सीपीयू रजिस्टर स्थान है, और कोई भी स्रोत कोड अभिव्यक्ति जो चर का संदर्भ मशीन कोड अनुक्रमों में अनुवादित होता है जो उस संदर्भ को संदर्भित करता है या सीपीयू रजिस्टर स्थान।

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

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

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

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

उन मौलिक आदिम प्रकारों से परे, हमें डेटा संरचनाओं में चीजों को सांकेतिक शब्दों में बदलना है और उन प्राइमेटिव के संदर्भ में उन्हें हेरफेर करने के लिए एल्गोरिदम का उपयोग करना है।

सी ++ में, बहुरूपता के लिए वर्ग पदानुक्रम में शामिल वस्तुओं में एक सूचक होता है, आमतौर पर ऑब्जेक्ट की शुरुआत में, यह एक वर्ग-विशिष्ट डेटा संरचना को संदर्भित करता है, जो आभासी प्रेषण, कास्टिंग, आदि के साथ मदद करता है।

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


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

@ पीटरए.साइडर, आप बिल्कुल सही हैं, चीजों की बड़ी तस्वीर में, लिंकर्स और लोडर भी स्रोत कोड से आए (वैश्विक) फ़ंक्शन और चर के नाम भाग लेते हैं और उपयोग करते हैं।
एरिक

एक अतिरिक्त जटिलता यह है कि कुछ संकलक नियमों की व्याख्या करते हैं, जो मानक के अनुसार, संकलक को यह मानने के लिए प्रेरित करते हैं कि कुछ चीजें अलग नहीं होंगी क्योंकि उन्हें अलग-अलग प्रकार के आपरेशनों को शामिल करने की अनुमति है, यहां तक ​​कि उन मामलों में भी जो लिखित रूप में अलियासिंग शामिल नहीं करते हैं । ऐसा कुछ दिया गया है useT1(&unionArray[i].member1); useT2(&unionArray[j].member2); useT1(&unionArray[i].member1);, क्लैंग और जीसीसी को यह अनुमान है कि दोनों एक ही से प्राप्त होने के बावजूद सूचक तक unionArray[j].member2नहीं पहुंच सकते unionArray[i].member1हैं unionArray[]
सुपरकैट

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

2

अगर मैं एक निश्चित प्रकार के चर को परिभाषित करता हूं तो यह किस प्रकार के चर का ट्रैक रखता है।

यहां दो प्रासंगिक चरण हैं:

  • संकलन समय

C संकलक मशीन भाषा के लिए C कोड संकलित करता है। संकलक के पास सभी जानकारी है कि वह आपकी स्रोत फ़ाइल (और पुस्तकालयों, और जो भी अन्य सामान को अपना काम करने की आवश्यकता है) से प्राप्त कर सकता है। सी कंपाइलर का मतलब क्या है का ट्रैक रखता है। C संकलक जानता है कि यदि आप एक चर घोषित करते हैं char, तो यह चार है।

यह एक तथाकथित "प्रतीक तालिका" का उपयोग करके ऐसा करता है जो चर, उनके प्रकार और अन्य जानकारी के नामों को सूचीबद्ध करता है। यह एक बल्कि जटिल डेटा संरचना है, लेकिन आप इसके बारे में सोच सकते हैं कि मानव-पठनीय नामों का क्या मतलब है। कंपाइलर से बाइनरी आउटपुट में, इस तरह का कोई चर नाम अब नहीं दिखाई देता है (यदि हम वैकल्पिक डिबग जानकारी को अनदेखा करते हैं जो प्रोग्रामर द्वारा अनुरोध किया जा सकता है)।

  • क्रम

संकलक का आउटपुट - संकलित निष्पादन योग्य - मशीन भाषा है, जिसे आपके ओएस द्वारा रैम में लोड किया गया है, और सीधे आपके सीपीयू द्वारा निष्पादित किया जाता है। मशीन की भाषा में, "टाइप" की कोई धारणा नहीं है - इसमें केवल कमांड हैं जो रैम में कुछ स्थान पर काम करते हैं। आदेशों वास्तव में एक निश्चित प्रकार वे के साथ काम क्या है (यानी, वहाँ एक मशीन भाषा आदेश "इन दो 16-बिट रैम स्थानों 0x100 और 0x521 पर संग्रहीत पूर्णांकों जोड़ें" हो सकता है), लेकिन वहाँ कोई जानकारी नहीं है कहीं भी प्रणाली है कि में उन स्थानों पर बाइट्स वास्तव में पूर्णांक का प्रतिनिधित्व कर रहे हैं। यहां सभी प्रकार की त्रुटियों से कोई सुरक्षा नहीं है।


अगर किसी भी तरह से आप "बाइट कोड ओरिएंटेड भाषाओं" के साथ C # या Java का जिक्र कर रहे हैं, तो पॉइंटर्स के पास उनके द्वारा छोड़े गए किसी भी तरह से नहीं है; इसके विपरीत: संकेत C # और Java में बहुत अधिक सामान्य हैं (और परिणामस्वरूप, जावा में सबसे आम त्रुटियों में से एक "NullPointerException" है)। उन्हें "संदर्भ" नाम दिया गया है, यह केवल शब्दावली का विषय है।
पीटर - मोनिका

@ पीटरए। श्नाइडर, निश्चित रूप से, NullPOINTERException है, लेकिन मेरे द्वारा उल्लेखित भाषाओं में एक संदर्भ और एक सूचक के बीच एक बहुत ही निश्चित अंतर है (जैसे जावा, रूबी, शायद सी #, यहां तक ​​कि कुछ हद तक पर्ल) - संदर्भ एक साथ चलते हैं उनके प्रकार प्रणाली, कचरा संग्रह, स्वचालित मेमोरी प्रबंधन आदि के साथ; यह आमतौर पर एक मेमोरी स्थान को स्पष्ट रूप से बताने के लिए भी संभव नहीं है (जैसे char *ptr = 0x123सी)। मेरा मानना ​​है कि शब्द "पॉइंटर" का मेरा उपयोग इस संदर्भ में बहुत स्पष्ट होना चाहिए। यदि नहीं, तो बेझिझक मुझे सिर चढ़ा दें और मैं जवाब में एक वाक्य जोड़ दूंगा।
AnoE

पॉइंटर्स C ++ में "टाइप सिस्टम के साथ एक साथ चलते हैं ;-)"। (वास्तव में, जावा के क्लासिक जेनेरिक C ++ की तुलना में कम दृढ़ता से टाइप किए गए हैं।) कचरा संग्रह एक विशेषता है, जिसे C ++ ने अनिवार्य नहीं करने का फैसला किया है, लेकिन इसे लागू करने के लिए कार्यान्वयन संभव है, और इसका इस बात से कोई लेना-देना नहीं है कि हम किस शब्द का उपयोग करते हैं।
पीटर -

ठीक है, @ पीटरए। श्नाइडर, मुझे नहीं लगता कि हम यहाँ स्तर प्राप्त कर रहे हैं। मैंने उस पैराग्राफ को हटा दिया है जहाँ मैंने पॉइंटर्स का उल्लेख किया है, यह उत्तर वैसे भी कुछ भी नहीं करता था।
एनओई

1

कुछ महत्वपूर्ण विशेष मामले हैं जहां C ++ रनटाइम पर एक प्रकार की दुकान करता है।

क्लासिक समाधान एक विभेदित संघ है: एक डेटा संरचना जिसमें कई प्रकार की वस्तु होती है, साथ ही एक क्षेत्र जो कहता है कि वर्तमान में किस प्रकार का है। एक टेम्प्लेटेड संस्करण C ++ मानक लाइब्रेरी में है std::variant। आम तौर पर, टैग एक होगा enum, लेकिन अगर आपको अपने डेटा के लिए सभी बिट स्टोरेज की आवश्यकता नहीं है, तो यह एक बिटफील्ड हो सकता है।

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

बात यह है कि यह यहाँ प्रासंगिक हो जाती है: प्रत्येक ofstreamके लिए एक सूचक होता है ofstreamआभासी मेज, प्रत्येक ifstreamके लिए ifstreamआभासी मेज, और इतने पर। क्लास पदानुक्रमों के लिए, वर्चुअल टेबल पॉइंटर टैग के रूप में कार्य कर सकता है जो प्रोग्राम को बताता है कि क्लास ऑब्जेक्ट किस प्रकार का है!

हालांकि भाषा मानक उन लोगों को नहीं बताता है जो कंपाइलर डिजाइन करते हैं कि उन्हें हुड के तहत रनटाइम कैसे लागू करना चाहिए, यह आप कैसे उम्मीद कर सकते हैं dynamic_castऔर typeofकाम कर सकते हैं।


"भाषा मानक कोडर्स को नहीं बताता है" आपको शायद इस बात पर जोर देना चाहिए कि "कोडर्स" सवाल में लोगों को gcc, clang, msvc, आदि लिख रहे हैं , न कि उन लोगों का उपयोग करके जो अपने C ++ को संकलित करते हैं।
कैलथ

@ कैलेठ अच्छा सुझाव!
डेविस्लोर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.