अशक्त मान कहाँ संग्रहीत हैं, या वे बिल्कुल संग्रहीत हैं?


39

मैं अशक्त मूल्यों या अशक्त संदर्भों के बारे में सीखना चाहता हूं।

उदाहरण के लिए मेरे पास Apple नामक एक वर्ग है और मैंने इसका एक उदाहरण बनाया है।

Apple myApple = new Apple("yummy"); // The data is stored in memory

फिर मैंने उस सेब को खाया और अब उसे अशक्त होने की आवश्यकता है, इसलिए मैंने इसे शून्य के रूप में सेट किया।

myApple = null;

इस कॉल के बाद, मैं भूल गया कि मैंने इसे खा लिया है और अब जांच करना चाहता हूं।

bool isEaten = (myApple == null);

इस कॉल के साथ, myApple का संदर्भ कहां है? एक विशेष सूचक मान शून्य है? यदि हां, अगर मेरे पास 1000 अशक्त वस्तुएं हैं, तो क्या वे 1000 ऑब्जेक्ट मेमोरी स्पेस या 1000 इंट मेमोरी स्पेस पर कब्जा कर लेते हैं अगर हम एक पॉइंटर प्रकार के बारे में सोचते हैं?

जवाबों:


45

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

यदि आप मूल रूप से 1000 संदर्भों को शून्य पर सेट करते हैं, तो आपके पास सिर्फ 1000 संदर्भों के लिए स्थान है, आमतौर पर 1000 * 4 बाइट्स (32-बिट सिस्टम पर, दो बार 64 पर)। यदि वे 1000 संदर्भ मूल रूप से वास्तविक वस्तुओं की ओर संकेत करते हैं, तो आपने प्रत्येक वस्तु के आकार का 1000 गुना, 1000 संदर्भों के लिए स्थान आवंटित किया है।

कुछ भाषाओं (जैसे C और C ++) में, पॉइंटर्स हमेशा किसी चीज़ की ओर इशारा करते हैं, यहाँ तक कि जब "अनइंस्टाल्यूटेड" होते हैं। मुद्दा यह है कि आपके द्वारा उपयोग किया जाने वाला पता आपके कार्यक्रम के लिए कानूनी है या नहीं। विशेष पता शून्य (उर्फ null) को जानबूझकर आपके पते के स्थान पर मैप नहीं किया जाता है, इसलिए मेमोरी मैनेजमेंट यूनिट (MMU) द्वारा एक्सेस किए जाने पर एक सेगमेंटेशन फॉल्ट उत्पन्न होता है और आपका प्रोग्राम क्रैश हो जाता है। लेकिन जब से पता शून्य को जानबूझकर मैप नहीं किया जाता है, यह इंगित करने के लिए उपयोग करने के लिए एक आदर्श मूल्य बन जाता है कि एक संकेतक कुछ भी इंगित नहीं कर रहा है, इसलिए इसकी भूमिका null। कहानी को पूरा करने के लिए, जैसा कि आप के साथ newया स्मृति को आवंटित करते हैंmalloc()ऑपरेटिंग सिस्टम रैम के पृष्ठों को आपके पते के स्थान में रैम के मानचित्र को कॉन्फ़िगर करता है और वे उपयोग करने योग्य बन जाते हैं। आमतौर पर पता स्थान की विशाल रेंज होती है जो मैप नहीं की जाती है, और इसलिए विभाजन दोष भी होते हैं।


बहुत अच्छी व्याख्या।
NoChance

6
यह "मेमोरी लीक" भाग पर थोड़ा गलत है। यह स्वचालित मेमोरी प्रबंधन के बिना सिस्टम में मेमोरी लीक है। हालाँकि, GC स्वचालित मेमोरी प्रबंधन को लागू करने का एकमात्र संभव तरीका नहीं है। C ++ std::shared_ptr<Apple>का एक उदाहरण है जो न तो जीसी है और न ही Appleशून्य होने पर लीक होता है।
MSalters

1
@MSalters - shared_ptrकचरा संग्रहण के लिए सिर्फ एक मूल रूप नहीं है ? जीसी की आवश्यकता नहीं है कि एक अलग "कचरा संग्रहकर्ता" हो, केवल वह कचरा संग्रह होता है।
मोनिका

5
@ ब्रेंडन: "कचरा संग्रह" शब्द को गैर-निर्धारक संग्रह के संदर्भ में लगभग समझा जाता है जो सामान्य कोड पथ से स्वतंत्र होता है। संदर्भ गणना के आधार पर नियतात्मक विनाश कुछ पूरी तरह से अलग है।
मेसन व्हीलर

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

13

उत्तर उस भाषा पर निर्भर करता है जिसका आप उपयोग कर रहे हैं।

C / C ++

C और C ++ में, कीवर्ड NULL था, और NULL वास्तव में क्या था 0. यह निर्णय लिया गया था कि "0x0000" कभी भी किसी ऑब्जेक्ट के लिए वैध पॉइंटर नहीं होने वाला था, और इसलिए यह मान है जिसे यह इंगित करने के लिए असाइन किया गया है कि यह वैध सूचक नहीं है। हालाँकि, यह पूरी तरह से मनमाना है। यदि आप इसे एक पॉइंटर की तरह एक्सेस करने का प्रयास करते हैं, तो यह बिल्कुल एक पॉइंटर की तरह एक ऑब्जेक्ट के साथ व्यवहार करेगा, जो अब मेमोरी में मौजूद नहीं है, जिससे एक अमान्य पॉइंटर अपवाद को फेंक दिया जाता है। सूचक स्वयं मेमोरी पर कब्जा कर लेता है, लेकिन पूर्णांक ऑब्जेक्ट से अधिक नहीं होगा। इसलिए, यदि आपके पास 1000 अशक्त बिंदु हैं, तो यह 1000 पूर्णांक के बराबर है। यदि उन बिंदुओं में से कुछ वैध वस्तुओं की ओर इशारा करते हैं, तो मेमोरी का उपयोग 1000 पूर्णांकों के बराबर होगा और साथ ही उन वैध ऑब्जेक्ट में निहित मेमोरी। याद रखें कि C या C ++ में,मैमोरी जारी नहीं की गई है, इसलिए आपको डीललोक (C) या डिलीट (C ++) का उपयोग करके स्पष्ट रूप से उस ऑब्जेक्ट को हटाना होगा।

जावा

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


9
आपका भेद सही नहीं है: वास्तव में, C और C ++ में, null पॉइंटर को मेमोरी एड्रेस 0 पर इंगित करने की आवश्यकता नहीं है (हालाँकि यह प्राकृतिक कार्यान्वयन है, जैसा कि जावा और C # में है)। यह शाब्दिक रूप से कहीं भी इंगित कर सकता है। यह इस तथ्य से थोड़ा उलझा हुआ है कि शाब्दिक -० को कथित तौर पर अशक्त सूचक में परिवर्तित किया जा सकता है। लेकिन एक अशक्त पॉइंटर के लिए संग्रहीत बिट पैटर्न अभी भी सभी शून्य नहीं होना चाहिए।
कोनराड रुडोल्फ

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

3
@ नील: एक शून्य सूचक स्थिरांक (पूर्णांक के प्रकार जो शून्य का मूल्यांकन करता है) का एक शून्य सूचक मान के लिए परिवर्तनीय है । (Ull4.10 C ++ 11।) एक शून्य सूचक मान सभी बिट्स शून्य होने की गारंटी नहीं है। 0एक अशक्त सूचक स्थिर है, लेकिन इसका मतलब यह नहीं है कि myptr == 0जाँच करता है कि सभी बिट्स myptrशून्य हैं।
चटाई

5
@ नील: आप C faq या इस SO प्रश्न में इस प्रविष्टि की जाँच करना चाहते हैं
hugomg

1
@ इसीलिए कि मुझे NULL"मैक्रो पॉइंटर" के बारे में बात करने के बजाय दर्द का ज़िक्र नहीं करना चाहिए , और यह स्पष्ट रूप से उल्लेख करना चाहिए कि "शाब्दिक -० को कथित तौर पर एक नेल पॉइंटर में बदला जा सकता है"।
कोनराड रुडोल्फ

5

एक पॉइंटर बस एक वैरिएबल है जो अधिकतर पूर्णांक प्रकार का होता है। यह एक स्मृति पता निर्दिष्ट करता है जहां वास्तविक वस्तु संग्रहीत होती है।

अधिकांश भाषाएं इस सूचक चर के माध्यम से ऑब्जेक्ट सदस्यों तक पहुंचने की अनुमति देती हैं:

int localInt = myApple.appleInt;

कंपाइलर जानता है कि ए के सदस्यों को कैसे एक्सेस करना है Apple। यह पॉइंटर के myAppleपते का "अनुसरण करता है" और का मान निकालता हैappleInt

यदि आप एक पॉइंटर वैरिएबल को null पॉइंटर असाइन करते हैं, तो आप पॉइंटर पॉइंट को बिना मेमोरी एड्रेस के बनाते हैं। (जिससे सदस्य की पहुंच असंभव हो जाती है।)

प्रत्येक पॉइंटर के लिए आपको मेमोरी एड्रेस पूर्णांक मान रखने के लिए मेमोरी की आवश्यकता होती है (ज्यादातर 32 बिट सिस्टम पर 4 बाइट्स, 64 बिट सिस्टम पर 8 बाइट्स)। यह अशक्त बिंदुओं के लिए भी सही है।


मुझे लगता है कि संदर्भ चर / वस्तुएं बिल्कुल संकेत नहीं हैं। यदि आप उन्हें प्रिंट करते हैं तो उनमें ClassName @ Hashcode होता है। जेवीएम आंतरिक रूप से हाशटे को वास्तविक पते के साथ संग्रहीत करने के लिए हैशटेबल का उपयोग करता है और आवश्यक होने पर वास्तविक पते को पुनः प्राप्त करने के लिए एक हैश एल्गोरिदम का उपयोग करता है।
minusSeven

@minusSeven पूर्णांकों की तरह शाब्दिक वस्तुओं की चिंता के लिए यह सही है। अन्यथा हैशटेबल Apple क्लास के भीतर निहित अन्य ऑब्जेक्ट्स की ओर संकेत करता है।
नील

@minusSeven: मैं सहमत हूं। सूचक कार्यान्वयन का विवरण भाषा / रनटाइम पर बहुत अधिक निर्भर करता है। लेकिन मुझे लगता है कि वे विवरण विशिष्ट प्रश्न के लिए प्रासंगिक नहीं हैं।
स्टीफन

4

त्वरित उदाहरण (ध्यान देने योग्य नाम संग्रहीत नहीं हैं):

void main()
{
  int X = 3;
  int *Y = X;
  int *Z = null;
} // void main(...)


...........................
....+-----+--------+.......
....|     |   X    |.......
....+-----+--------+.......
....| 100 |   3    |<---+..
....+-----+--------+....|..
........................|..
....+-----+--------+....|..
....|     |   Y    |....|..
....+-----+--------+....|..
....| 102 |  100   +----+..
....+-----+--------+.......
...........................
....+-----+--------+.......
....|     |   z    |.......
....+-----+--------+.......
....| 104 |   0    |.......
....+-----+--------+.......
...........................

चीयर्स।

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