C # संदर्भ और एक सूचक के बीच क्या अंतर है?


85

मैं सी # संदर्भ और एक सूचक के बीच के अंतर को काफी नहीं समझता। वे दोनों स्मृति में एक जगह की ओर इशारा करते हैं न? एकमात्र अंतर जो मैं समझ सकता हूं कि पॉइंटर्स उतना चालाक नहीं है, ढेर पर कुछ भी इंगित नहीं कर सकते हैं, कचरा संग्रह से मुक्त हैं, और केवल संदर्भ संरचना या आधार प्रकार को संदर्भित कर सकते हैं।

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

मुझे अभी नहीं मिला कि एक पॉइंटर के बारे में इतना जटिल क्या है? यह मूल रूप से सिर्फ एक जगह है जो स्मृति में है? यह अपना स्थान वापस कर सकता है और उस स्थान पर मौजूद वस्तु से सीधे बातचीत कर सकता है?

क्या मैं एक बड़े पैमाने पर चूक गया?


1
संक्षिप्त उत्तर हाँ है, आपने कुछ महत्वपूर्ण याद किया है, और इसका कारण "... धारणा है कि लोगों को संकेत समझने की आवश्यकता है"। संकेत: C # वहाँ से बाहर एकमात्र भाषा नहीं है।
jdigital

जवाबों:


50

C # संदर्भ, और कचरा संग्राहक द्वारा स्थानांतरित किया जा सकता है लेकिन सामान्य संकेत स्थिर हैं। यही कारण है कि हम fixedकीवर्ड को किसी एलीमेंट एलीमेंट में प्राप्त करने के लिए उपयोग करते हैं, ताकि इसे ले जाने से रोका जा सके।

संपादित करें: वैचारिक रूप से, हाँ। वे कमोबेश एक जैसे हैं।


क्या कोई अन्य कमांड नहीं है जो C # संदर्भ को उस वस्तु से रोकता है जिसे वह संदर्भ है जिसे GC द्वारा स्थानांतरित किया गया है?
रिचर्ड

क्षमा करें, मुझे लगा कि यह कुछ और था क्योंकि पोस्ट ने एक पॉइंटर को संदर्भित किया था।
रिचर्ड

हां, एक GCHandle.Alloc, या एक मार्शल ।llocHGlobal (तय से परे)
ctacke

यह सी # में ठीक किया गया है, C ++ / CLI में pin_ptr
Mehrdad Afshari

मार्शल। ऑलोकहॉडल स्मृति को प्रबंधित ढेर में आवंटित नहीं करेगा और स्वाभाविक रूप से यह कचरा संग्रह के अधीन नहीं है।
मेहरदाद अफश्री

130

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

उदाहरण के लिए निम्न कोड लें

int* p1 = GetAPointer();

यह इस अर्थ में सुरक्षित है कि GetAPointer को int * के साथ संगत प्रकार वापस करना चाहिए। फिर भी अभी भी कोई गारंटी नहीं है कि * p1 वास्तव में एक इंट की ओर इशारा करेगा। यह रैंडम मेमोरी में चार, डबल या सिर्फ पॉइंटर हो सकता है।

एक संदर्भ हालांकि एक विशिष्ट वस्तु की ओर इशारा करता है। ऑब्जेक्ट को स्मृति में इधर-उधर ले जाया जा सकता है लेकिन संदर्भ को अमान्य नहीं किया जा सकता है (जब तक कि आप असुरक्षित कोड का उपयोग नहीं करते हैं)। संकेत की तुलना में इस संदर्भ में संदर्भ अधिक सुरक्षित हैं।

string str = GetAString();

इस स्थिति में str में दो में से एक अवस्था 1 है) यह किसी वस्तु की ओर इशारा करता है और इसलिए अशक्त है या 2) यह वैध स्ट्रिंग की ओर इशारा करता है। बस। CLR इस मामले की गारंटी देता है। यह पॉइंटर के लिए नहीं कर सकता है और न ही करेगा।


14
महान व्याख्या।
iTayb

13

एक संदर्भ एक "सार" सूचक है: आप एक संदर्भ के साथ अंकगणित नहीं कर सकते हैं और आप इसके मूल्य के लिए किसी भी निम्न-स्तरीय चाल को नहीं खेल सकते हैं।


8

एक संदर्भ और एक सूचक के बीच एक बड़ा अंतर यह है कि एक संकेतक बिट्स का एक संग्रह है, जिसकी सामग्री केवल तब ही मायने रखती है जब यह सक्रिय रूप से एक पॉइंटर के रूप में उपयोग किया जा रहा हो, जबकि एक संदर्भ न केवल बिट्स के एक सेट को एन्क्रिप्ट करता है, बल्कि कुछ मेटाडेटा भी रखता है जो रखता है अंतर्निहित ढांचे ने इसके अस्तित्व की जानकारी दी। यदि कोई पॉइंटर मेमोरी में किसी ऑब्जेक्ट में मौजूद होता है, और वह ऑब्जेक्ट डिलीट हो जाता है, लेकिन पॉइंटर को मिटाया नहीं जाता है, तो पॉइंटर के निरंतर अस्तित्व को तब तक कोई नुकसान नहीं होगा जब तक कि मेमोरी को एक्सेस करने का प्रयास नहीं किया जाता है। यदि पॉइंटर का उपयोग करने का कोई प्रयास नहीं किया जाता है, तो कुछ भी इसके अस्तित्व की परवाह नहीं करेगा। इसके विपरीत, .NET या JVM जैसे संदर्भ-आधारित ढांचे की आवश्यकता है कि सिस्टम के लिए हमेशा अस्तित्व में प्रत्येक वस्तु संदर्भ की पहचान करना संभव हो, और अस्तित्व में प्रत्येक वस्तु संदर्भ हमेशा होना चाहिएnull या इसके उचित प्रकार की एक वस्तु की पहचान करें।

ध्यान दें कि प्रत्येक वस्तु संदर्भ वास्तव में दो प्रकार की सूचनाओं को संलग्न करता है: (1) जिस वस्तु की पहचान करता है, उस क्षेत्र की सामग्री और (2) उसी वस्तु के अन्य संदर्भों का समुच्चय। यद्यपि ऐसा कोई तंत्र नहीं है जिसके द्वारा प्रणाली किसी वस्तु के लिए मौजूद सभी संदर्भों को जल्दी से पहचान सके, अन्य संदर्भों का समूह जो किसी वस्तु के लिए होता है, अक्सर संदर्भ द्वारा समझाया गया सबसे महत्वपूर्ण चीज हो सकता है (यह विशेष रूप से सच है जब प्रकार की चीजों का Objectउपयोग लॉक टोकन जैसी चीजों के रूप में किया जाता है)। यद्यपि सिस्टम उपयोग के लिए प्रत्येक वस्तु के लिए कुछ बिट डेटा रखता है GetHashCode, वस्तुओं का कोई वास्तविक पहचान नहीं है जो उनके संदर्भ में मौजूद है। यदि Xकिसी ऑब्जेक्ट का एकमात्र मौजूदा संदर्भ प्रतिस्थापित करता हैXउसी फ़ील्ड सामग्री के साथ एक नई वस्तु के संदर्भ में, बिट्स को वापस करने के लिए बदलने के अलावा कोई पहचानने योग्य प्रभाव नहीं होगाGetHashCode(), और यहां तक ​​कि उस प्रभाव की गारंटी नहीं है।


5

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

इसके अलावा, संकेत केवल असुरक्षित संदर्भ में उपयोग करने योग्य हैं।


5

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

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


सरणी की बात दिलचस्प लगती है, क्या यह है कि मूल रूप से आप सूचक को एक सरणी की तरह स्मृति स्थान को ऑफसेट करने के लिए कह सकते हैं, जबकि आप एक संदर्भ के साथ ऐसा करने में असमर्थ हैं? सोच नहीं सकते कि यह कब उपयोगी होगा लेकिन दिलचस्प कोई नहीं।
रिचर्ड

यदि p एक int * (int का सूचक) है, तो (p + 1) p + 4 बाइट्स (एक int का आकार) द्वारा पहचाना गया पता है। और p [1] * (p + 1) के समान है (अर्थात, यह "dereferences" पता 4 बाइट्स प्रति p) है। इसके विपरीत, एक सरणी संदर्भ (सी # में) के साथ, [] ऑपरेटर एक फ़ंक्शन कॉल करता है।
पी डैडी

5

पहले मुझे लगता है कि आपको अपने सेमैटिक्स में एक "पॉइंटर" को परिभाषित करने की आवश्यकता है। क्या आप का मतलब है कि आप उस असुरक्षित कोड को बना सकते हैं जिसे आप तय कर सकते हैं ? क्या आपका मतलब एक इंटप्रट है जो आपको शायद एक देशी कॉल या मार्शल से मिला है । क्या आपका मतलब GCHandle है ? सभी अनिवार्य रूप से एक ही चीज हैं - एक स्मृति पते का प्रतिनिधित्व जहां कुछ संग्रहीत किया जाता है - यह एक वर्ग, एक संख्या, एक संरचना, जो भी हो। और रिकॉर्ड के लिए, वे निश्चित रूप से ढेर पर हो सकते हैं।

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

दूसरी ओर एक संदर्भ बहुत ज्यादा एक "प्रबंधित सूचक" है जो जीसी के बारे में जानता है। यह अभी भी एक ऑब्जेक्ट का एक पता है, लेकिन अब GC लक्ष्य का विवरण जानता है, इसलिए यह इसे इधर-उधर कर सकता है, कर सकता है, अंतिम रूप दे सकता है, निपटान कर सकता है और अन्य सभी अच्छे सामानों को प्रबंधित वातावरण करता है।

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

संपादित करें: वास्तव में यहां एक अच्छा उदाहरण है जब आप प्रबंधित कोड में "पॉइंटर" का उपयोग कर सकते हैं - इस मामले में यह एक GCHandle है, लेकिन सटीक एक ही काम AllocHGlobal के साथ या एक बाइट या संरचना पर तय का उपयोग करके किया जा सकता है। मुझे लगता है कि यह मेरे लिए अधिक ".NET" लगता है GCHandle becasue पसंद करते हैं।


एक मामूली वक्रोक्ति जो शायद आपको "प्रबंधित सूचक" यहां नहीं कहना चाहिए - यहां तक ​​कि डरे हुए उद्धरणों के साथ भी - क्योंकि यह आईएल में एक वस्तु संदर्भ से काफी अलग है। यद्यपि C ++ / CLI में प्रबंधित पॉइंटर्स के लिए वाक्यविन्यास है, लेकिन वे आमतौर पर C # से सुलभ नहीं हैं। IL में, उन्हें (यानी) ldloca और ldarga निर्देशों के साथ प्राप्त किया जाता है।
ग्लेन स्लैडेन

5

एक सूचक आवेदन के पते स्थान में किसी भी बाइट को इंगित कर सकता है। किसी संदर्भ को .NET वातावरण द्वारा कसकर नियंत्रित और नियंत्रित किया जाता है।


1

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


1

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

पॉइंटर्स की अक्सर 'बदसूरत' होने के लिए आलोचना की जाती है।

class* myClass = new class();

अब हर बार जब आप इसका उपयोग करते हैं तो आपको इसे पहले या तो इसे रोकना पड़ता है

myClass->Method() or (*myClass).Method()

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

मेरे लिए यही कारण है कि बिंदुओं के समान लाभ प्रदान करने के लिए संदर्भों को पहले स्थान पर 'जन्म' दिया गया था, लेकिन सभी के बिना सूचक सिंटैक्स। अब आप वास्तविक वस्तु को पारित कर सकते हैं (केवल इसके मूल्य को नहीं) और आपके पास वस्तु के साथ बातचीत करने का एक अधिक पठनीय, सामान्य तरीका है।

MyMethod(&type parameter)
{
   parameter.DoThis()
   parameter.DoThat()
}

C ++ के संदर्भ C # / Java संदर्भों से भिन्न होते हैं, जिसमें एक बार जब आप इसे मान देते हैं कि यह था, तो आप इसे फिर से असाइन नहीं कर सकते थे (और जब यह घोषित किया गया था तो इसे सौंपा जाना है)। यह एक कास्ट पॉइंटर (एक ऐसा पॉइंटर जो किसी अन्य ऑब्जेक्ट पर दोबारा इंगित नहीं किया जा सकता था) का उपयोग करने के समान था।

जावा और सी # बहुत उच्च स्तर की हैं, आधुनिक भाषाएं जो पिछले वर्षों में C / C ++ में जमा हुई बहुत सारी गंदगी को साफ करती थीं और संकेत निश्चित रूप से उन चीजों में से एक थीं, जिन्हें 'साफ' करने की आवश्यकता थी।

जहां तक ​​आपकी बात जानने वाले संकेत के बारे में आपकी टिप्पणी आपको एक मजबूत प्रोग्रामर बनाती है, यह ज्यादातर मामलों में सच है। यदि आप जानते हैं कि 'कैसे' कुछ काम करता है, तो यह जानने के बिना कि मैं इसका उपयोग कर सकता हूं, यह कहकर कि यह आपको बढ़त दे सकता है। एक किनारे का कितना हिस्सा हमेशा अलग होगा। आखिरकार, बिना यह जाने कि इसे कैसे लागू किया जाता है, OOP और इंटरफेसेस की कई सुंदरियों में से एक है।

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

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

# 2: बहुरूपता / इंटरफेस जब आपके पास एक संदर्भ होता है जो एक इंटरफ़ेस प्रकार होता है और यह किसी ऑब्जेक्ट की ओर इशारा करता है, तो आप केवल उस इंटरफ़ेस के तरीकों को कॉल कर सकते हैं, भले ही ऑब्जेक्ट में कई और क्षमताएं हों। ऑब्जेक्ट अलग-अलग तरीकों से भी लागू हो सकते हैं।

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

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


1
व्यक्तिगत रूप से, मुझे लगता है कि C # एक बेहतर भाषा होती यदि उपयोग .किए जाने वाले अधिकांश स्थान ->, लेकिन foo.bar(123)स्थैतिक पद्धति के लिए एक कॉल का पर्यायवाची होते fooClass.bar(ref foo, 123)। कि चीजों की अनुमति होगी myString.Append("George"); [जो संशोधित करेगा चर myString ], और के बीच अर्थ में और अधिक स्पष्ट फर्क पड़ा myStruct.field = 3;और myClassObject->field = 3;
सुपरकाट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.