पॉइंटर्स एक अवधारणा है जो कई लोगों के लिए पहली बार भ्रमित करने वाली हो सकती है, विशेष रूप से जब यह आसपास के पॉइंटर मानों की नकल करने की बात आती है और अभी भी उसी मेमोरी ब्लॉक का संदर्भ दे रही है।
मैंने पाया है कि सबसे अच्छा सादृश्य सूचक को कागज के टुकड़े के रूप में उस पर एक घर के पते के रूप में विचार करना है, और मेमोरी इसे वास्तविक घर के रूप में संदर्भित करता है। इस प्रकार सभी प्रकार के कार्यों को आसानी से समझाया जा सकता है।
मैंने नीचे कुछ डेल्फी कोड जोड़ा है, और कुछ टिप्पणियां जहां उपयुक्त हैं। मैंने अपनी अन्य मुख्य प्रोग्रामिंग भाषा सी # के बाद से डेल्फी को चुना, उसी तरह से मेमोरी लीक जैसी चीजों को प्रदर्शित नहीं करता है।
यदि आप केवल उच्च-स्तरीय अवधारणा को सीखना चाहते हैं, तो आपको नीचे दिए गए स्पष्टीकरण में "मेमोरी लेआउट" लेबल वाले भागों को अनदेखा करना चाहिए। उनका उद्देश्य उन उदाहरणों को देना है जो ऑपरेशन के बाद स्मृति की तरह दिख सकते हैं, लेकिन वे प्रकृति में अधिक निम्न-स्तर के हैं। हालाँकि, यह समझाने के लिए कि बफर वास्तव में कैसे काम करता है, यह महत्वपूर्ण था कि मैंने इन आरेखों को जोड़ा।
डिस्क्लेमर: सभी इरादों और उद्देश्यों के लिए, यह स्पष्टीकरण और उदाहरण स्मृति लेआउट को काफी सरल बनाया गया है। यदि आपको निम्न-स्तर के आधार पर मेमोरी से निपटने की आवश्यकता है, तो अधिक ओवरहेड और बहुत अधिक विवरण आपको जानना होगा। हालांकि, स्मृति और संकेत की व्याख्या करने के इरादे के लिए, यह काफी सटीक है।
मान लेते हैं कि नीचे प्रयुक्त THouse वर्ग इस तरह दिखता है:
type
THouse = class
private
FName : array[0..9] of Char;
public
constructor Create(name: PChar);
end;
जब आप हाउस ऑब्जेक्ट को इनिशियलाइज़ करते हैं, तो कंस्ट्रक्टर को दिया गया नाम प्राइवेट फील्ड FName में कॉपी किया जाता है। एक कारण है कि इसे एक निश्चित आकार के सरणी के रूप में परिभाषित किया गया है।
स्मृति में, घर के आवंटन के साथ जुड़े कुछ ओवरहेड होंगे, मैं इसे इस तरह समझाऊंगा:
--- [ttttNNNNNNNNNN] ---
^ ^
| |
| + - FName सरणी
|
+ - ओवरहेड
"टेट्ट" क्षेत्र ओवरहेड है, इसमें आम तौर पर विभिन्न प्रकार के रनटाइम्स और भाषाओं के लिए अधिक होगा, जैसे 8 या 12 बाइट्स। यह जरूरी है कि इस क्षेत्र में जो भी मूल्य संचित हैं, वे मेमोरी एलोकेटर या कोर सिस्टम रूटीन के अलावा किसी भी चीज से कभी नहीं बदले जाते हैं, या आप प्रोग्राम को क्रैश करने का जोखिम उठाते हैं।
स्मृति आवंटित करें
अपना घर बनाने के लिए एक उद्यमी प्राप्त करें, और आपको घर का पता दें। वास्तविक दुनिया के विपरीत, मेमोरी आवंटन को यह नहीं बताया जा सकता है कि कहां आवंटित करना है, लेकिन पर्याप्त जगह के साथ एक उपयुक्त स्थान मिलेगा, और आवंटित मेमोरी को पता वापस रिपोर्ट करेगा।
दूसरे शब्दों में, उद्यमी मौके का चयन करेगा।
THouse.Create('My house');
मेमोरी लेआउट:
--- [ttttNNNNNNNNNN] ---
1234 मेरा घर
पते के साथ एक चर रखें
कागज के एक टुकड़े पर अपने नए घर का पता लिखें। यह पेपर आपके घर के संदर्भ के रूप में काम करेगा। कागज के इस टुकड़े के बिना, आप खो गए हैं, और घर नहीं पा सकते हैं, जब तक कि आप पहले से ही इसमें न हों।
var
h: THouse;
begin
h := THouse.Create('My house');
...
मेमोरी लेआउट:
ज
v
--- [ttttNNNNNNNNNN] ---
1234 मेरा घर
पॉइंटर मान कॉपी करें
बस कागज के एक नए टुकड़े पर पता लिखें। अब आपके पास कागज के दो टुकड़े हैं जो आपको एक ही घर में मिलेंगे, दो अलग-अलग घरों में नहीं। एक पेपर से पते का पालन करने और उस घर में फर्नीचर को फिर से व्यवस्थित करने के किसी भी प्रयास से ऐसा लगेगा कि दूसरे घर को उसी तरीके से संशोधित किया गया है, जब तक कि आप स्पष्ट रूप से पता नहीं लगा सकते कि यह वास्तव में सिर्फ एक घर है।
नोट यह आमतौर पर अवधारणा है कि मुझे लोगों को समझाने में सबसे अधिक समस्या है, दो बिंदुओं का मतलब दो वस्तुओं या मेमोरी ब्लॉकों से नहीं है।
var
h1, h2: THouse;
begin
h1 := THouse.Create('My house');
h2 := h1; // copies the address, not the house
...
h1
v
--- [ttttNNNNNNNNNN] ---
1234 मेरा घर
^
h2
स्मृति को मुक्त करना
घर में तोड़फोड़ की। आप बाद में एक नए पते के लिए कागज का पुन: उपयोग कर सकते हैं यदि आप चाहते हैं, या इसे घर के पते को भूल जाने के लिए साफ़ करें कि अब मौजूद नहीं है।
var
h: THouse;
begin
h := THouse.Create('My house');
...
h.Free;
h := nil;
यहां मैं पहली बार घर का निर्माण करता हूं, और इसके पते को पकड़ता हूं। फिर मैं घर के लिए कुछ करता हूं (इसका उपयोग, ... कोड, पाठक के लिए एक अभ्यास के रूप में छोड़ दिया), और फिर मैंने इसे मुक्त कर दिया। अंत में मैंने अपने चर से पता साफ कर दिया।
मेमोरी लेआउट:
एच <- +
v + - मुफ्त में
--- [ttttNNNNNNNNNN] --- |
1234 मेरा घर <- +
h (अब अंक कहीं नहीं) <- +
+ - मुफ्त के बाद
---------------------- | (ध्यान दें, मेमोरी अभी भी हो सकती है
xx34 मेरा घर <- + में कुछ डेटा होता है)
नुकीले बिंदु
आप अपने उद्यमी को घर को नष्ट करने के लिए कहते हैं, लेकिन आप अपने कागज के टुकड़े से पते को मिटाना भूल जाते हैं। जब बाद में आप कागज के टुकड़े को देखते हैं, तो आप भूल गए हैं कि घर अब नहीं है, और असफल परिणामों के साथ इसे देखने के लिए जाता है (नीचे एक अमान्य संदर्भ के बारे में भी हिस्सा देखें)।
var
h: THouse;
begin
h := THouse.Create('My house');
...
h.Free;
... // forgot to clear h here
h.OpenFrontDoor; // will most likely fail
h
कॉल के बाद का उपयोग .Free
कर काम हो सकता है , लेकिन यह सिर्फ शुद्ध भाग्य है। सबसे अधिक संभावना है कि यह एक महत्वपूर्ण ऑपरेशन के बीच में ग्राहकों की जगह पर विफल हो जाएगा।
एच <- +
v + - मुफ्त में
--- [ttttNNNNNNNNNN] --- |
1234 मेरा घर <- +
एच <- +
v + - मुफ्त के बाद
---------------------- |
xx34 मेरा घर <- +
जैसा कि आप देख सकते हैं, एच अभी भी स्मृति में डेटा के अवशेष को इंगित करता है, लेकिन चूंकि यह पूरा नहीं हो सकता है, इससे पहले कि यह विफल हो।
स्मृति रिसाव
आप कागज का टुकड़ा खो देते हैं और घर नहीं पा सकते हैं। यह घर हालांकि अभी भी कहीं खड़ा है, और जब आप बाद में एक नया घर बनाना चाहते हैं, तो आप उस स्थान का पुन: उपयोग नहीं कर सकते।
var
h: THouse;
begin
h := THouse.Create('My house');
h := THouse.Create('My house'); // uh-oh, what happened to our first house?
...
h.Free;
h := nil;
यहां हम h
एक नए घर के पते के साथ चर की सामग्री को ओवररोट करते हैं , लेकिन पुराना अभी भी खड़ा है ... कहीं। इस कोड के बाद, उस घर तक पहुंचने का कोई रास्ता नहीं है, और इसे खड़ा छोड़ दिया जाएगा। दूसरे शब्दों में, आवंटित मेमोरी तब तक रहेगी जब तक कि आवेदन बंद नहीं हो जाता है, जिस बिंदु पर ऑपरेटिंग सिस्टम इसे फाड़ देगा।
पहले आवंटन के बाद मेमोरी लेआउट:
ज
v
--- [ttttNNNNNNNNNN] ---
1234 मेरा घर
दूसरे आवंटन के बाद मेमोरी लेआउट:
ज
v
--- [ttttNNNNNNNNNN] --- [ttttNNNNNNNNNN]
1234 मेरा घर 5678 घर
इस विधि को प्राप्त करने का एक और अधिक सामान्य तरीका यह है कि किसी चीज़ को मुक्त करना भूल जाए, बजाय इसके ऊपर लिखे हुए। डेल्फी के संदर्भ में, यह निम्नलिखित विधि के साथ होगा:
procedure OpenTheFrontDoorOfANewHouse;
var
h: THouse;
begin
h := THouse.Create('My house');
h.OpenFrontDoor;
// uh-oh, no .Free here, where does the address go?
end;
इस पद्धति को निष्पादित करने के बाद, हमारे चर में कोई स्थान नहीं है कि घर का पता मौजूद है, लेकिन घर अभी भी बाहर है।
मेमोरी लेआउट:
एच <- +
v + - पॉइंटर खोने से पहले
--- [ttttNNNNNNNNNN] --- |
1234 मेरा घर <- +
h (अब अंक कहीं नहीं) <- +
+ - पॉइंटर खोने के बाद
--- [ttttNNNNNNNNNN] --- |
1234 मेरा घर <- +
जैसा कि आप देख सकते हैं, पुराने डेटा को मेमोरी में बरकरार रखा गया है, और मेमोरी एलोकेटर द्वारा पुन: उपयोग नहीं किया जाएगा। आवंटनकर्ता इस बात का ध्यान रखता है कि स्मृति के किन क्षेत्रों का उपयोग किया गया है, और जब तक आप इसे मुक्त नहीं करते, तब तक उनका पुन: उपयोग नहीं करेंगे।
स्मृति को मुक्त करना लेकिन एक (अब अमान्य) संदर्भ रखना
घर को ध्वस्त करें, कागज के टुकड़ों में से एक को मिटा दें, लेकिन आपके पास पुराने पते के साथ कागज का एक और टुकड़ा भी है, जब आप पते पर जाते हैं, तो आपको घर नहीं मिलेगा, लेकिन आपको कुछ ऐसा मिल सकता है जो खंडहर जैसा दिखता है में से एक।
शायद आपको एक घर भी मिल जाएगा, लेकिन यह वह घर नहीं है जिसे आपको मूल रूप से पता दिया गया था, और इस तरह इसका उपयोग करने का कोई भी प्रयास हालांकि यह आपके लिए बहुत बुरी तरह से विफल हो सकता है।
कभी-कभी आप यह भी पाते हैं कि एक पड़ोसी के पते पर एक बड़ा घर स्थापित होता है जो तीन पते (मेन स्ट्रीट 1-3) पर कब्जा कर लेता है, और आपका पता घर के मध्य में चला जाता है। एक छोटे से घर के रूप में बड़े 3-एड्रेस हाउस के उस हिस्से का इलाज करने का कोई भी प्रयास बुरी तरह से विफल हो सकता है।
var
h1, h2: THouse;
begin
h1 := THouse.Create('My house');
h2 := h1; // copies the address, not the house
...
h1.Free;
h1 := nil;
h2.OpenFrontDoor; // uh-oh, what happened to our house?
यहां घर को संदर्भ के माध्यम से फाड़ दिया गया था h1
, और जब h1
भी साफ किया गया था, तब h2
भी पुराना, पुराना, पता है। उस घर तक पहुंच जो अब खड़ा नहीं है या काम नहीं कर सकता है।
यह ऊपर झूलने वाले सूचक का एक रूपांतर है। इसकी मेमोरी लेआउट देखें।
बफर ओवररन
आप घर में अधिक सामान ले जा सकते हैं, जितना संभव है कि आप फिट हो सकते हैं, पड़ोसी के घर या यार्ड में जा सकते हैं। जब उस पड़ोसी घर का मालिक बाद में घर आता है, तो वह उन सभी प्रकार की चीजों को खोज लेगा, जिन पर वह खुद विचार करेगा।
यही कारण है कि मैंने एक निश्चित आकार की सरणी को चुना। चरण निर्धारित करने के लिए, मान लें कि दूसरा घर जिसे हम आवंटित करते हैं, किसी कारण से, स्मृति में पहले एक के सामने रखा जाएगा। दूसरे शब्दों में, दूसरे घर में पहले वाले से कम पता होगा। इसके अलावा, वे एक दूसरे के ठीक बगल में आवंटित किए गए हैं।
इस प्रकार, यह कोड:
var
h1, h2: THouse;
begin
h1 := THouse.Create('My house');
h2 := THouse.Create('My other house somewhere');
^-----------------------^
longer than 10 characters
0123456789 <-- 10 characters
पहले आवंटन के बाद मेमोरी लेआउट:
h1
v
----------------------- [ttttNNNNNNNNNN]
5678 मेरा घर
दूसरे आवंटन के बाद मेमोरी लेआउट:
एच 2 एच 1
vv
--- [ttttNNNNNNNNNN] ---- [ttttNNNNNNNNNN]
1234 अन्य घर कहीं
^ --- + - ^
|
+ - अधिलेखित
वह हिस्सा जो अक्सर दुर्घटना का कारण बनेगा, जब आप अपने द्वारा संग्रहीत डेटा के महत्वपूर्ण हिस्सों को ओवरराइट करते हैं जो वास्तव में बेतरतीब ढंग से नहीं बदलना चाहिए। उदाहरण के लिए, यह एक समस्या नहीं हो सकती है कि एच 1-हाउस के नाम के कुछ हिस्सों को प्रोग्राम को क्रैश करने के संदर्भ में बदल दिया गया था, लेकिन जब आप टूटी हुई वस्तु का उपयोग करने का प्रयास करते हैं, तो ऑब्जेक्ट के ओवरहेड को ओवरराइट करना सबसे अधिक दुर्घटना की संभावना होगी। ओवरराइटिंग लिंक जो ऑब्जेक्ट में अन्य ऑब्जेक्ट्स में संग्रहीत होता है।
लिंक की गई सूची
जब आप कागज के एक टुकड़े पर एक पते का पालन करते हैं, तो आप एक घर में पहुंचते हैं, और उस घर में कागज का एक और टुकड़ा होता है, जिस पर एक नया पता होता है, श्रृंखला के अगले घर के लिए, और इसी तरह।
var
h1, h2: THouse;
begin
h1 := THouse.Create('Home');
h2 := THouse.Create('Cabin');
h1.NextHouse := h2;
यहां हम अपने घर के घर से हमारे केबिन तक एक लिंक बनाते हैं। हम श्रृंखला का अनुसरण कर सकते हैं जब तक कि घर में कोई NextHouse
संदर्भ न हो, जिसका अर्थ है कि यह अंतिम है। हमारे सभी घरों का दौरा करने के लिए, हम निम्नलिखित कोड का उपयोग कर सकते हैं:
var
h1, h2: THouse;
h: THouse;
begin
h1 := THouse.Create('Home');
h2 := THouse.Create('Cabin');
h1.NextHouse := h2;
...
h := h1;
while h <> nil do
begin
h.LockAllDoors;
h.CloseAllWindows;
h := h.NextHouse;
end;
मेमोरी लेआउट (ऑब्जेक्ट में एक लिंक के रूप में नेक्स्टहाउस जोड़ा गया है, नीचे दिए गए आरेख में चार LLLL के साथ नोट किया गया है):
एच 1 एच 2
vv
--- [ttttNNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL]
1234Home + 5678 कैबिन +
| ^ |
+ -------- + * (कोई लिंक नहीं)
मूल शब्दों में, मेमोरी एड्रेस क्या है?
एक स्मृति पता बुनियादी शब्दों में सिर्फ एक संख्या है। यदि आप स्मृति को बाइट्स के एक बड़े सरणी के रूप में सोचते हैं, तो पहले बाइट का पता 0 है, अगला पता 1 है और इसी तरह ऊपर की तरफ। यह सरलीकृत है, लेकिन काफी अच्छा है।
तो यह मेमोरी लेआउट:
एच 1 एच 2
vv
--- [ttttNNNNNNNNNN] --- [ttttNNNNNNNNNN]
1234 मेरा घर 5678 घर
ये दो पते हो सकते हैं (सबसे बाईं ओर - पता 0 है):
जिसका अर्थ है कि ऊपर सूचीबद्ध हमारी सूची इस तरह दिख सकती है:
h1 (= 4) h2 (= 28)
vv
--- [ttttNNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL]
1234Home 0028 5678 कैबिन 0000
| ^ |
+ -------- + * (कोई लिंक नहीं)
यह एक पते को संग्रहीत करने के लिए विशिष्ट है जो "अंक कहीं नहीं" एक शून्य-पते के रूप में है।
मूल शब्दों में, एक सूचक क्या है?
एक पॉइंटर एक मेमोरी एड्रेस रखने वाला एक वेरिएबल है। आप आमतौर पर प्रोग्रामिंग भाषा को अपना नंबर देने के लिए कह सकते हैं, लेकिन अधिकांश प्रोग्रामिंग भाषाएं और रनटाइम इस तथ्य को छिपाने की कोशिश करते हैं कि नीचे एक नंबर है, सिर्फ इसलिए कि नंबर ही वास्तव में आपके लिए कोई अर्थ नहीं रखता है। एक पॉइंटर को ब्लैक बॉक्स के रूप में सोचना सबसे अच्छा है, अर्थात। आप वास्तव में नहीं जानते हैं या परवाह नहीं है कि यह वास्तव में कैसे लागू किया जाता है, जब तक यह काम करता है।