पॉइंटर का लक्ष्य और साझा ऑब्जेक्ट
मान बदलने से पहले लक्ष्य पहचानें
कॉपी किया गया पूर्णांक अपना अलग मान रखता है। पॉइंटर के मान की कॉपी होने पर दो पॉइंटर चर एक ही पूर्णांक ऑब्जेक्ट की ओर हो सकते हैं। बाद में होने वाले लेखन को समझने के लिए दो रिकॉर्ड चाहिए: पूर्णांक ऑब्जेक्ट में रखी संख्याएँ और पॉइंटर के वर्तमान लक्ष्य।
इस पाठ में हम int के सरल पॉइंटर, एड्रेस लेने वाला & और डीरेफ़रेंस करने वाला * सीखेंगे। आप पॉइंटर चर, उसमें रखे पॉइंटर मान और उसके लक्ष्य को अलग पहचानेंगे। साझा लक्ष्य और लक्ष्य बदलने का ट्रेस करेंगे तथा नल पॉइंटर और ऑब्जेक्ट के जीवनकाल की सीमाएँ समझेंगे।
पहले से आवश्यक बातें: इनिशियलाइज़ किए पूर्णांक चर, क्रम से होने वाले असाइनमेंट, मानों की कॉपी, if और शॉर्ट सर्किट। पिछले पाठों की ऐरे संबंधी जानकारी उपयोगी संदर्भ है, लेकिन यहाँ हर लक्ष्य एक साधारण स्केलर पूर्णांक है। अभी पॉइंटर अंकगणित, ऐरे से पॉइंटर में रूपांतरण, पॉइंटर पैरामीटर, मेमोरी आवंटन या मशीन एड्रेस का उपयोग नहीं होगा।
पढ़ाने के लिए हम C11 का उपयोग जारी रखते हैं; GATE पाठ्यक्रम ने इस संस्करण को निर्धारित नहीं किया है। निश्चित व्यवहार वाले पूरे प्रोग्रामों की सारी अंकगणित −32767 से 32767 के भीतर रहती है। हर प्रोग्राम नई शुरुआत वाला उदाहरण है। अभ्यास के दो असुरक्षित अंश केवल वर्गीकरण के लिए हैं; आउटपुट का अनुमान लगाने के लिए उन्हें कंपाइल या निष्पादित न करें।
पॉइंटर चर में पॉइंटर मान होता है
main के भीतर घोषणाएँ int score = 7; और int *p = &score; देखें।
scoreएक पूर्णांक ऑब्जेक्ट है, जिसका वर्तमान मान 7 हैpएक अलग पॉइंटर चर है, जिसका प्रकार “intका पॉइंटर” हैpमें रखा मानscoreको पहचानता है। उसका लक्ष्य हम प्रतीक के रूप मेंp → scoreलिखते हैं
पॉइंटर चर स्वयं भी एक ऑब्जेक्ट है, लेकिन वह उस पूर्णांक ऑब्जेक्ट से अलग है जिसकी ओर वह जाता है। C में पॉइंटर प्रकार भी स्केलर प्रकार हैं: p में एक पॉइंटर मान होता है। इससे पॉइंटर मान हमारी अंकगणित में इस्तेमाल होने वाला साधारण पूर्णांक नहीं बन जाता।
&score में &, score की ओर पॉइंटर देता है; वह पूर्णांक 7 पढ़कर 7 को एड्रेस में नहीं बदलता। 1000 जैसा कोई काल्पनिक एड्रेस लेने की आवश्यकता नहीं है। हमारा तीर केवल ऑब्जेक्ट की पहचान बताता है, बाइट की संख्या, भौतिक स्थान, आपसी दूरी या मशीन में रखने का क्रम नहीं। तीर चित्र बनाने का तरीका है, C का सिंटैक्स नहीं।
घोषणा int *p = &score; में *, p को पॉइंटर घोषित करने का हिस्सा है। बाद के व्यंजक में *p, वर्तमान पॉइंटर मान के लक्ष्य को चुनता है। p → score होने पर *p पढ़ने से score का वर्तमान पूर्णांक मिलता है। *p = 12; जैसा असाइनमेंट score में 12 लिखता है। इससे p का लक्ष्य नहीं बदलता।
इसके विपरीत, किसी दूसरे जीवित पूर्णांक other के लिए p = &other;, p में रखा पॉइंटर मान बदलता है। वह किसी भी पूर्णांक में नहीं लिखता। अगली बार डीरेफ़रेंस करने से पहले तीर अपडेट करें।
| दर्ज करने वाली बात | इस शुरुआती उदाहरण में | इससे किस प्रश्न का उत्तर मिलता है |
|---|---|---|
| पूर्णांक ऑब्जेक्ट और मान | score = 7 | कौन-सी संख्या रखी है? |
| पॉइंटर चर और मान | p में &score से मिला पॉइंटर है | इस अलग चर में क्या है? |
| वर्तमान लक्ष्य | p → score | *p कौन-सा पूर्णांक चुनेगा? |
सीखते समय हर पॉइंटर की अलग घोषणा लिखें। केवल घोषणा होने से लक्ष्य मौजूद होने की गारंटी नहीं मिलती। बिना इनिशियलाइज़र घोषित automatic पॉइंटर, जैसे int *p;, को उपयोग योग्य लक्ष्य या निश्चित नल मान नहीं मिलता। उपयोग करने से पहले उसे ज्ञात मान दें।
पॉइंटर मान की कॉपी से लक्ष्य साझा रह सकता है
int *q = p; के बाद दोनों तीर score पर पहुँचते हैं: p → score ← q। पॉइंटर चर p और q अलग ही रहते हैं। इस इनिशियलाइज़ेशन में पॉइंटर मान की कॉपी होती है, पूर्णांक ऑब्जेक्ट की नहीं। इससे q, चर p की ओर भी नहीं हो जाता।
इसे साझा लक्ष्य या एलियसिंग कहते हैं: अभी व्यंजक *p और *q एक ही पूर्णांक ऑब्जेक्ट तक पहुँचते हैं। किसी भी व्यंजक से लिखने पर उसी एक ऑब्जेक्ट का मान बदलता है। इसलिए दूसरे व्यंजक से बाद में पढ़ने पर अपडेट किया हुआ मान मिलता है।
इसकी तुलना int saved = *p; से करें। यहाँ इनिशियलाइज़र एक पूर्णांक पढ़कर अलग पूर्णांक चर को शुरुआती मान देता है। saved में संख्या आती है, लक्ष्य से लगातार जुड़ाव नहीं। यह बुनियादी पाठों के मान-कॉपी वाले मॉडल का ही पालन है। मुख्य बात यह है कि कॉपी किस मान की हुई: पूर्णांक की या पॉइंटर की।
हर स्टेटमेंट के लिए:
*pया*qका लक्ष्य चुनने से पहले वर्तमान तीर देखें- जाँचें कि जिस लक्ष्य तक पहुँचना है वह जीवित और उपयुक्त ऑब्जेक्ट है तथा पढ़े जाने वाले हर पूर्णांक का मान ज्ञात है
- पूर्णांकों के वर्तमान मानों से गणना करें
- केवल एक गंतव्य अपडेट करें:
*p = ...में पूर्णांक मान, याp = ...में तीर - बाकी सभी संख्याएँ और तीर ज्यों के त्यों आगे रखें
हल किया हुआ उदाहरण 1 एक पूर्णांक तक पहुँचने के दो रास्ते
आउटपुट बताएँ। p से पहले लेखन के बाद भी क्या q से 7 पढ़ा जाएगा? क्या saved बाद के अपडेट के साथ बदलेगा?
#include <stdio.h>
int main(void)
{
int score = 7;
int *p = &score;
int *q = p;
int saved = *p;
*p = *p + 5;
*q = saved + *q;
printf("%d %d %d\n", score, *q, saved);
return 0;
}| इस चरण के बाद | score | saved | p का लक्ष्य | q का लक्ष्य |
|---|---|---|---|---|
| सभी घोषणाएँ | 7 | 7 | score | score |
*p = *p + 5 | 12 | 7 | score | score |
*q = saved + *q | 19 | 7 | score | score |
pको&scoreसे शुरुआती मान मिलता है;qउसी पॉइंटर मान की कॉपी करता है। चित्र हैp → score ← qsaved,pके रास्ते 7 पढ़ता है और फिर उस पूर्णांक की कॉपी रखता है- पहला लेखन वर्तमान 7 पढ़ता है, उसमें 5 जोड़ता है और
scoreमें 12 लिखता है। कोई तीर नहीं बदलता - दूसरा लेखन
saved = 7और वर्तमान*q = 12पढ़कर उसीscoreमें 19 लिखता है - अंतिम लक्ष्य-चित्र अब भी
p → score ← qहै और अबscore = 19है। आउटपुट:19 19 7, फिर नई पंक्ति
12 14 7 बताने पर आपने q को पुराने पूर्णांक की अलग कॉपी दे दी, जबकि उसमें पॉइंटर मान की कॉपी हुई थी। 24 24 24 बताने पर saved अपनी कॉपी रखने के बजाय लक्ष्य के अपडेट के साथ बदल रहा है। ट्रेस में पॉइंटर मान और सहेजे गए पूर्णांक मान की अलग प्रविष्टियाँ रखने से दोनों गलतियाँ दूर होती हैं। साझा ऑब्जेक्ट अब भी एक ही पूर्णांक है; दो अलग पॉइंटर चरों से लिखने पर उसके दो अलग संस्करण नहीं बनते।
हल किया हुआ उदाहरण 2 एक पॉइंटर का लक्ष्य बदलें और दूसरे का बनाए रखें
अब दो पूर्णांक ऑब्जेक्ट हैं। p में दोबारा असाइनमेंट करने पर q का तीर अपने-आप नहीं बदलना चाहिए।
#include <stdio.h>
int main(void)
{
int left = 3;
int right = 11;
int *p = &left;
int *q = p;
p = &right;
*p = *q + 2;
*q = *p + 4;
printf("%d %d %d %d\n", left, right, *p, *q);
return 0;
}| इस चरण के बाद | left | right | p का लक्ष्य | q का लक्ष्य |
|---|---|---|---|---|
| सभी घोषणाएँ | 3 | 11 | left | left |
p = &right | 3 | 11 | right | left |
*p = *q + 2 | 3 | 5 | right | left |
*q = *p + 4 | 9 | 5 | right | left |
शुरुआत में p → left ← q है और right अलग है। लक्ष्य बदलने के बाद चित्र p → right और q → left है। अभी कोई पूर्णांक नहीं बदला है।
पहला लेखन q के रास्ते 3 पढ़ता है, उसमें 2 जोड़ता है और p के रास्ते right में 5 लिखता है। अगला लेखन p के रास्ते नया 5 पढ़ता है, उसमें 4 जोड़ता है और q के रास्ते left में 9 लिखता है। आउटपुट: 9 5 5 9, फिर नई पंक्ति।
यदि p = &right पर आपने दोनों तीर बदल दिए, तो q = p को पॉइंटर चरों के बीच स्थायी जुड़ाव मान लिया। वह एक बार हुई मान की कॉपी थी। यदि लक्ष्य बदलते समय left को 11 कर दिया, तो पॉइंटर असाइनमेंट को *p = right समझ लिया। दोनों के गंतव्य अलग हैं। बाद का लेखन किस पूर्णांक तक पहुँचेगा, यह वर्तमान लक्ष्य तय करते हैं, पॉइंटर चर के नाम नहीं।
नल मान स्पष्ट रूप से बताता है कि लक्ष्य नहीं है
हमारे उदाहरणों में NULL इस्तेमाल करते समय <stddef.h> शामिल करें। int *p = NULL; पॉइंटर को ज्ञात नल मान से इनिशियलाइज़ करता है। हम चित्र में p → कोई लक्ष्य नहीं लिखते हैं। पॉइंटर चर मौजूद है, लेकिन उसके इस मान से कोई ऐसा पूर्णांक ऑब्जेक्ट नहीं मिलता जिसे *p से पढ़ा या लिखा जा सके।
नल पॉइंटर और शून्य रखे हुए पूर्णांक का पॉइंटर अलग हैं। int value = 0; और int *p = &value; के बाद चित्र p → value है तथा value = 0 है। *p पढ़ना सही है और उससे 0 मिलता है। p = NULL होने पर *p से पढ़ना या उसमें लिखना गलत है, चाहे आप कोई भी संख्या पढ़ना या रखना चाहते हों। NULL से यह निष्कर्ष न निकालें कि सभी बाइट शून्य होंगे या कोई खास भौतिक एड्रेस होगा।
जब यह ज्ञात हो कि p या तो नल है या किसी उपयुक्त जीवित पूर्णांक का पॉइंटर है, तब if (p != NULL) अपनी body में डीरेफ़रेंस से पहले गार्ड लगा सकता है। नल होने पर body छोड़ दी जाती है; सही लक्ष्य होने पर body उस लक्ष्य का उपयोग कर सकती है। लेखन छोड़ देने से लक्ष्य नहीं बनता और आवश्यक अपडेट भी नहीं होता।
p != NULL && *p > 0 में भी यही क्रम महत्वपूर्ण है: उसी ज्ञात-मान वाली शर्त के अंतर्गत, बाईं तुलना नल होने पर दायाँ ऑपरेंड चलने नहीं देती। ऑपरेंड उलट देने पर नल की जाँच से पहले पढ़ने की कोशिश होगी। नल से तुलना किसी भी मनमाने पॉइंटर की सुरक्षा जाँचने का सामान्य तरीका नहीं है। विशेष रूप से, यह बिना इनिशियलाइज़ किए पॉइंटर या उस पॉइंटर को ठीक नहीं करती जिसका लक्ष्य समाप्त हो चुका हो।
लक्ष्य जीवित रहना चाहिए
इन उदाहरणों का साधारण स्थानीय int, automatic storage duration वाला है। यदि वह भीतर के ब्लॉक का है, तो उस ब्लॉक से निष्पादन बाहर निकलते ही उसका जीवनकाल समाप्त हो जाता है। बाहरी ब्लॉक का पॉइंटर चर उस पूर्णांक से अधिक समय तक जीवित रह सकता है जिसे वह पहले पहचानता था। पॉइंटर चर बचा रहने से लक्ष्य ऑब्जेक्ट जीवित नहीं रहता।
लक्ष्य के ट्रेस में बंद होने वाले ब्रेस पर भीतर के ऑब्जेक्ट को समाप्त चिह्नित करें। जिस पॉइंटर में अब भी उसी ऑब्जेक्ट की ओर पॉइंटर था, उसका मान C11 के अनुसार उस सीमा पर अनिर्धारित हो जाता है। वह अपने-आप नल नहीं बनता। उसे उपयोग योग्य तीर की तरह आगे न दिखाएँ, डीरेफ़रेंस न करें और पुराने मान की NULL से तुलना करके उसे सही साबित करने की कोशिश न करें।
एक सुरक्षित तरीका है कि ब्लॉक छोड़ने से पहले लक्ष्य का काम पूरा करें और आवश्यक पूर्णांक परिणाम को अधिक समय तक जीवित रहने वाले चर में कॉपी कर लें। यदि अधिक समय तक रहने वाले पॉइंटर की अब जरूरत नहीं है, तो उसके लक्ष्य के जीवित रहते ही उसमें NULL रख दें। एक पॉइंटर साफ करने से उस ऑब्जेक्ट के दूसरे पॉइंटर साफ नहीं होते; हर पॉइंटर का अपना मान होता है। एक और तरीका लक्ष्य को ऐसे ब्लॉक में रखना है जो उसके सभी जरूरी उपयोगों तक चले। नीचे का अभ्यास पहला तरीका अपनाता है।
जीवनकाल और वर्तमान संख्या दो अलग बातें हैं। स्रोत ऑब्जेक्ट समाप्त होने के बाद भी पूर्णांक की कॉपी सही रह सकती है; पॉइंटर की कॉपी उस ऑब्जेक्ट को बचाए नहीं रखती। यह अंतर समझने के लिए “पुराने बाइट अब भी वहीं होंगे” जैसी धारणा की जरूरत नहीं है।
हल देखने से पहले अभ्यास करें
नई स्थितियों के इन चार मौलिक अभ्यासों के अंक नहीं हैं। इनमें सीखी हुई समझ लागू करें। निश्चित व्यवहार वाले प्रोग्रामों के लिए आउटपुट, बीच की पूर्णांक अवस्थाएँ और लक्ष्य-चित्र दें। अभ्यास 3 और 4 के असुरक्षित अंशों को बिना कंपाइल या निष्पादित किए वर्गीकृत करें, फिर बताया गया सुधार करें।
अभ्यास 1 बराबर संख्याएँ और नया साझा लक्ष्य
क्या शुरुआत में p और q एक ही ऑब्जेक्ट को पहचानते हैं? दिखाएँ कि q = p पर क्या बदलता है, फिर आउटपुट दें।
#include <stdio.h>
int main(void)
{
int first = 6;
int second = 6;
int *p = &first;
int *q = &second;
*p = 9;
q = p;
*q = *q - 2;
printf("%d %d %d %d\n", first, second, *p, *q);
return 0;
}अभ्यास 2 पूर्णांक की कॉपी और पॉइंटर में दो बदलाव
हर गंतव्य का ट्रेस करें। समझाएँ कि q = p संख्या 10 की कॉपी क्यों नहीं करता और उसके बाद p का लक्ष्य बदलने से q का लक्ष्य क्यों नहीं बदलता।
#include <stdio.h>
int main(void)
{
int red = 4;
int blue = 10;
int *p = &red;
int *q = &blue;
int saved = *q;
*p = *q;
q = p;
p = &blue;
*q = *q - 3;
*p = saved + *q;
printf("%d %d %d %d %d\n", red, blue, saved, *p, *q);
return 0;
}अभ्यास 3 आवश्यक लक्ष्य दें
उद्देश्य p के रास्ते लिखकर total के मान 8 में 3 जोड़ना है। मान लें कि <stddef.h> शामिल है और यह अंश main के भीतर है। यह जानबूझकर असुरक्षित है; इसे केवल पढ़ें।
int total = 8; int *p = NULL; *p = total + 3;
लेखन को वर्गीकृत करें। p की घोषणा और लेखन के बीच एक पॉइंटर असाइनमेंट डालें, जिससे बताया हुआ अपडेट सही हो जाए। समझाएँ कि मूल लेखन को केवल if (p != NULL) का गार्ड देने से उद्देश्य पूरा क्यों नहीं होगा।
अभ्यास 4 भीतर का ऑब्जेक्ट समाप्त होने के बाद परिणाम रखें
मान लें कि सामान्य हेडर और बाहर का main मौजूद हैं। यह अंश जानबूझकर असुरक्षित है; इसे कंपाइल या निष्पादित न करें।
int saved = 0;
int *p = NULL;
{
int local = 9;
p = &local;
*p = *p + 2;
saved = *p;
}
printf("%d %d\n", saved, *p);ब्लॉक के भीतर सही काम का ट्रेस करें। किस सीमा पर p में local की ओर उपयोग योग्य पॉइंटर नहीं रहता? क्या C11 आउटपुट के रूप में 11 11 तय करता है? local और उसका अपडेट भीतर के ब्लॉक में रखते हुए अंश सुधारें: अंतिम पूर्णांक saved में रखें, उस ब्लॉक को छोड़ने से पहले p साफ करें और बाद में केवल saved प्रिंट करें।
पूरे हल और गलत उत्तरों का कारण
अभ्यास 1 का हल
| इस चरण के बाद | first | second | p का लक्ष्य | q का लक्ष्य |
|---|---|---|---|---|
| सभी घोषणाएँ | 6 | 6 | first | second |
*p = 9 | 9 | 6 | first | second |
q = p | 9 | 6 | first | first |
*q = *q - 2 | 7 | 6 | first | first |
शुरुआती चित्र p → first और q → second है। रखी संख्याएँ बराबर होने से दो ऑब्जेक्ट एक नहीं हो जाते। पहला लेखन केवल first बदलता है। फिर q = p केवल q का लक्ष्य बदलता है: चित्र p → first ← q हो जाता है और second में अब भी 6 है। अंतिम लेखन q के रास्ते 9 पढ़ता है, उसमें से 2 घटाता है और first में 7 लिखता है। आउटपुट: 7 6 7 7, फिर नई पंक्ति।
7 7 7 7 दोनों पूर्णांक ऑब्जेक्ट को केवल इसलिए गलत ढंग से जोड़ देता है क्योंकि उनके शुरुआती मान बराबर थे। 9 4 9 4 पॉइंटर असाइनमेंट को छोड़ देता है और q का लक्ष्य अब भी second मानता है। पॉइंटर का कॉपी हुआ मान कॉपी होते समय ही दर्ज करना चाहिए।
अभ्यास 2 का हल
| इस चरण के बाद | red | blue | saved | p का लक्ष्य | q का लक्ष्य |
|---|---|---|---|---|---|
| सभी घोषणाएँ | 4 | 10 | 10 | red | blue |
*p = *q | 10 | 10 | 10 | red | blue |
q = p | 10 | 10 | 10 | red | red |
p = &blue | 10 | 10 | 10 | blue | red |
*q = *q - 3 | 7 | 10 | 10 | blue | red |
*p = saved + *q | 7 | 17 | 10 | blue | red |
पहला लेखन पूर्णांक 10 की कॉपी blue से red में करता है; चित्र p → red और q → blue ही रहता है। फिर q = p पॉइंटर मान की कॉपी करता है, जिससे p → red ← q मिलता है। वह *p या *q नहीं पढ़ता। केवल p का लक्ष्य बदलने पर p → blue और q → red मिलता है।
इसलिए घटाव 10 - 3 = 7 को red में लिखता है। अंतिम लेखन सहेजा हुआ पूर्णांक 10 और वर्तमान red = 7 पढ़ता है, फिर blue में 17 लिखता है। आउटपुट: 7 17 10 17 7, फिर नई पंक्ति।
saved को 17 कर देना पूर्णांक की कॉपी को एलियस मानना है। घटाव से blue बदलना, p के बाद वाले असाइनमेंट के साथ q को भी बदल देना है। *p = *q को तीर का बदलाव मानना, लक्ष्य में रखे मान की कॉपी और पॉइंटर मान की कॉपी को मिला देना है। बाईं ओर का * ही गंतव्य तय करने में महत्वपूर्ण है।
अभ्यास 3 का हल
शुरुआत में total = 8 और p → कोई लक्ष्य नहीं है। दाईं ओर की अंकगणित 8 + 3 सीमा में है, लेकिन उससे लेखन का गंतव्य नहीं मिलता। *p = ... के लिए नल पॉइंटर को डीरेफ़रेंस करने पर अपरिभाषित व्यवहार होता है। C11 किसी अपडेट, खास आउटपुट या क्रैश की गारंटी नहीं देता।
लेखन से पहले p = &total; डालें। पूरा सुधरा हुआ प्रोग्राम है:
#include <stddef.h>
#include <stdio.h>
int main(void)
{
int total = 8;
int *p = NULL;
p = &total;
*p = total + 3;
printf("%d %d\n", total, *p);
return 0;
}- घोषणाओं के बाद:
total = 8,p → कोई लक्ष्य नहीं p = &totalके बाद:total = 8,p → total- लेखन 8 पढ़ता है, 11 निकालता है और उसे
totalमें रखता है। अबtotal = 11,p → totalहै - प्रिंट होने वाले दोनों व्यंजक उसी जीवित पूर्णांक को पढ़ते हैं। आउटपुट:
11 11, फिर नई पंक्ति
फिर से p = NULL करने पर भी लक्ष्य नहीं मिलेगा। *p = 0 पॉइंटर को इनिशियलाइज़ नहीं कर सकता; वह लक्ष्य में एक और गलत लेखन का प्रयास होगा। मूल लेखन पर if (p != NULL) का गार्ड लगाने से इस ज्ञात-नल स्थिति में लेखन सुरक्षित रूप से छोड़ दिया जाएगा, लेकिन total = 8 ही रहेगा। इसलिए माँगा गया अपडेट पूरा नहीं होगा। पहले p = &total करना केवल ऑपरेशन छोड़ने के बजाय उसका लक्ष्य देता है।
अभ्यास 4 का हल
पूरे असुरक्षित अंश का संख्यात्मक आउटपुट तय किए बिना उसके सही शुरुआती भाग का ट्रेस करें:
| जाँच-बिंदु | saved | भीतर का local | p की अवस्था |
|---|---|---|---|
| भीतर के ब्लॉक से पहले | 0 | अभी जीवित नहीं | ज्ञात नल |
local के इनिशियलाइज़ेशन के बाद | 0 | 9 | ज्ञात नल |
p = &local के बाद | 0 | 9 | p → local |
*p = *p + 2 के बाद | 0 | 11 | p → local |
saved = *p के बाद | 11 | 11 | p → local |
| भीतर का ब्लॉक छोड़ने के बाद | 11 | जीवनकाल समाप्त | अनिर्धारित; पुराना लक्ष्य उपयोग योग्य नहीं |
भीतर पढ़ना और लिखना सही है। बंद होने वाला ब्रेस local का जीवनकाल समाप्त करता है, भले ही saved और पॉइंटर चर p बाहरी ब्लॉक में जीवित रहें। बाद का *p उस समाप्त ऑब्जेक्ट को नहीं पढ़ सकता। अंश में अपरिभाषित व्यवहार है, इसलिए C11 11 11 तय नहीं करता और पहले प्रिंट होने वाले 11 की भी गारंटी नहीं देता। सही शुरुआती भाग का ठीक ट्रेस, अपरिभाषित व्यवहार वाले पूरे निष्पादन के आउटपुट की गारंटी नहीं है।
बताया गया सुधार है:
#include <stddef.h>
#include <stdio.h>
int main(void)
{
int saved = 0;
int *p = NULL;
{
int local = 9;
p = &local;
*p = *p + 2;
saved = *p;
p = NULL;
}
printf("%d\n", saved);
return 0;
}सुधार में saved = 11 होने तक वही सही अवस्थाएँ आती हैं। फिर, जब local मान 11 के साथ अब भी जीवित है, p = NULL पॉइंटर मान बदलता है: p → कोई लक्ष्य नहीं। इससे local या saved नहीं बदलता। भीतर का ब्लॉक समाप्त होने पर p ज्ञात नल ही रहता है और saved में 11 बना रहता है। प्रिंट केवल अधिक समय तक जीवित रहने वाले पूर्णांक की कॉपी पढ़ता है। आउटपुट: 11, फिर नई पंक्ति।
मूल प्रिंट से पहले नल की जाँच जोड़ना सुधार नहीं है: जीवनकाल की सीमा के बाद मूल p अनिर्धारित है, न कि नल और जीवित लक्ष्य के बीच कोई ज्ञात विकल्प। ब्लॉक समाप्त होने से पहले p को दूसरे पॉइंटर में कॉपी करने पर उस दूसरे पॉइंटर में भी वही जीवनकाल की समस्या रहेगी। पूर्णांक परिणाम रखना और समाप्त ऑब्जेक्ट की ओर एक और पॉइंटर रखना अलग बातें हैं।
आगे बढ़ने से पहले
p = q और *p = *q के अलग गंतव्य समझाएँ। p को q में कॉपी करने और फिर केवल p का लक्ष्य बदलने के बाद दोनों तीर बनाएँ। अंत में समझाएँ कि पूर्णांक सहेजने से ब्लॉक की सीमा के बाद भी परिणाम क्यों रह सकता है, जबकि उस ब्लॉक के स्थानीय पूर्णांक का पॉइंटर सहेजने से लक्ष्य क्यों नहीं बचता। आगे हम कॉपी किए आर्ग्युमेंट का मॉडल बनाए रखते हुए इन लक्ष्य-रिकॉर्ड को फ़ंक्शन कॉल से जोड़ेंगे।
स्रोत टिप्पणी
उदाहरण, चित्र, ट्रेस, अभ्यास और समझाने वाले वाक्य मौलिक हैं। अर्थ संबंधी तथ्यों की जाँच WG14 N1570 C11 समिति-ड्राफ्ट से की गई: पॉइंटर और स्केलर प्रकार §§6.2.5p20–21; एड्रेस लेना और डीरेफ़रेंस §§6.5.3.2p3–4; पॉइंटर घोषणाएँ §6.7.6.1p1; स्केलर इनिशियलाइज़ेशन §§6.7.9p10–11; असाइनमेंट §6.5.16.1p2; नल पॉइंटर §6.3.2.3p3 और NULL §7.19p3; पॉइंटर और नल की तुलना §§6.5.9p2, p5–6; शॉर्ट सर्किट §6.5.13p4; automatic जीवनकाल §§6.2.4p2, p5–6।
C11 पढ़ाने के लिए चुना गया संस्करण है, GATE की बताई अनिवार्यता नहीं। यह पाठ स्केलर पॉइंटर के लक्ष्य का सीमित परिचय है, सभी पॉइंटर, पूरी C भाषा या पूरा GATE CS नहीं।
सूत्र और नियम
int *p = &x;पॉइंटर चर बनाता है और उसे जीवित पूर्णांकxकी ओर इनिशियलाइज़ करता है- पूर्णांक मान और पॉइंटर लक्ष्य अलग दर्ज करें; तीर प्रतीक हैं, मशीन एड्रेस नहीं
*pवर्तमान लक्ष्य चुनता है। पढ़ने पर लक्ष्य का पूर्णांक मिलता है; उसमें असाइनमेंट करने पर वही पूर्णांक बदलता हैp = &yकिसी भी पूर्णांक को बदले बिनाpका लक्ष्य बदलता हैq = pवर्तमान पॉइंटर मान की कॉपी करता है। इससे साझा लक्ष्य बन सकता है, लेकिनpके भविष्य के बदलावों के साथqनहीं बदलताint saved = *p;पूर्णांक की कॉपी करता है; लक्ष्य में बाद के अपडेट सेsavedनहीं बदलता- ज्ञात नल पॉइंटर का डीरेफ़रेंस योग्य लक्ष्य नहीं होता। शून्य रखे हुए पूर्णांक का पॉइंटर अलग है
- नल गार्ड ज्ञात और मान्य पॉइंटर मान की शर्त पर केवल नल को बाहर करता है; वह सामान्य पॉइंटर वैधता सिद्ध नहीं करता
- automatic लक्ष्य अपने हर अभिगम तक जीवित रहना चाहिए। उसके जीवनकाल के अंत से पहले आवश्यक पूर्णांक परिणाम सहेजें
- गलत डीरेफ़रेंस वर्गीकरण के प्रश्न हैं; दिख गया आउटपुट उन्हें सही नहीं बना सकता
इस पाठ के नोट्स
प्रगति सहेजने के लिए साइन इन करें। साइन इन