संकेत समझने के लिए क्या बाधाएं हैं और उन्हें दूर करने के लिए क्या किया जा सकता है? [बन्द है]


449

कई नए, और यहां तक ​​कि पुराने, कॉलेज स्तर के छात्रों के लिए C या C ++ में भ्रम के प्रमुख कारक क्यों हैं? क्या कोई उपकरण या विचार प्रक्रियाएं हैं जो आपको यह समझने में मदद करती हैं कि संकेत चर, फ़ंक्शन और स्तर से परे कैसे काम करते हैं?

कुछ अच्छी अभ्यास चीजें क्या हैं जो किसी को "अह-हह, मुझे मिल गया" के स्तर पर लाने के लिए किया जा सकता है, बिना उन्हें समग्र अवधारणा में उलझाए हुए? असल में, परिदृश्य की तरह ड्रिल।


20
इस प्रश्न की थीसिस यह है कि संकेत समझना मुश्किल है। यह प्रश्न इस बात का कोई प्रमाण नहीं देता है कि किसी भी चीज़ की तुलना में संकेत समझना कठिन हैं।
bmargulies

14
शायद मुझे कुछ याद आ रहा है (क्योंकि मैं GCC'd भाषाओं में कोड हूँ) लेकिन मैंने हमेशा सोचा था कि यदि की-मेमोरी मान के रूप में पॉइंटर्स मेमोरी में हो। चूंकि किसी प्रोग्राम में बड़ी मात्रा में डेटा को पास करना महंगा है, आप संरचना (मूल्य) बनाते हैं और इसके चारों ओर पॉइंटर / संदर्भ (कुंजी) बनाते हैं क्योंकि कुंजी बड़ी संरचना का बहुत छोटा प्रतिनिधित्व है। कठिन हिस्सा वह है जब आपको दो बिंदुओं / संदर्भों की तुलना करने की आवश्यकता होती है (क्या आप कुंजियों या मूल्यों की तुलना कर रहे हैं) जिन्हें संरचना (मूल्य) के भीतर निहित डेटा में तोड़ने के लिए अधिक काम करने की आवश्यकता होती है।
इवान प्लाइस

2
@ वोल्फपैक'०8 "मुझे लगता है कि संबोधन में एक स्मृति हमेशा एक इंट होगी।" - तब यह आपको प्रतीत होना चाहिए कि कुछ भी प्रकार नहीं है, क्योंकि वे सभी स्मृति में केवल बिट्स हैं। "वास्तव में, सूचक के प्रकार वर के लिए सूचक अंक के प्रकार है" - नहीं, सूचक के प्रकार है करने के लिए सूचक जो स्वाभाविक है और स्पष्ट होना चाहिए - सूचक अंक के वर के प्रकार।
जिम बाल्टर

2
मैं हमेशा सोचता था कि इस बात को समझना कितना कठिन है कि चर (और कार्य) स्मृति के ब्लॉक हैं और संकेत स्मृति के भंडारण वाले चर हैं। यह शायद बहुत व्यावहारिक विचार मॉडल अमूर्त अवधारणाओं के सभी प्रशंसकों को प्रभावित नहीं कर सकता है, लेकिन यह पूरी तरह से समझने में मदद करता है कि पॉइंटर्स कैसे काम करते हैं।
क्रिश्चियन राऊ

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

जवाबों:


745

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

मैंने पाया है कि सबसे अच्छा सादृश्य सूचक को कागज के टुकड़े के रूप में उस पर एक घर के पते के रूप में विचार करना है, और मेमोरी इसे वास्तविक घर के रूप में संदर्भित करता है। इस प्रकार सभी प्रकार के कार्यों को आसानी से समझाया जा सकता है।

मैंने नीचे कुछ डेल्फी कोड जोड़ा है, और कुछ टिप्पणियां जहां उपयुक्त हैं। मैंने अपनी अन्य मुख्य प्रोग्रामिंग भाषा सी # के बाद से डेल्फी को चुना, उसी तरह से मेमोरी लीक जैसी चीजों को प्रदर्शित नहीं करता है।

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

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


मान लेते हैं कि नीचे प्रयुक्त 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 = 23

जिसका अर्थ है कि ऊपर सूचीबद्ध हमारी सूची इस तरह दिख सकती है:

    h1 (= 4) h2 (= 28)
    vv
--- [ttttNNNNNNNNNNLLLL] ---- [ttttNNNNNNNNNNLLLL]
    1234Home 0028 5678 कैबिन 0000
                   | ^ |
                   + -------- + * (कोई लिंक नहीं)

यह एक पते को संग्रहीत करने के लिए विशिष्ट है जो "अंक कहीं नहीं" एक शून्य-पते के रूप में है।


मूल शब्दों में, एक सूचक क्या है?

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


59
बफ़र ओवररन उल्लसित है। "पड़ोसी घर आता है, अपने कबाड़ पर अपनी खोपड़ी खुली फिसलता है, और तुम्हें गुमनामी में मुकदमा करता है।"
gtd

12
यह अवधारणा की एक अच्छी व्याख्या है, निश्चित रूप से। अवधारणा यह नहीं है कि मैं बिंदुओं के बारे में भ्रामक हूं, हालांकि यह पूरा निबंध थोड़ा व्यर्थ था।
ब्रेटन

10
लेकिन सिर्फ यह पूछने के लिए कि आपको पॉइंटर्स के बारे में भ्रामक क्या लगता है?
लास वी। कार्लसन

11
जब से आपने उत्तर लिखा है मैंने इस पोस्ट को कई बार संशोधित किया है। कोड के साथ आपका स्पष्टीकरण उत्कृष्ट है, और मैं आपको अधिक विचारों को जोड़ने / परिष्कृत करने के लिए इसे फिर से शुरू करने की सराहना करता हूं। ब्रावो लास!
डेविड मैकग्रा

3
पाठ का एक भी पन्ना नहीं है (कोई फर्क नहीं पड़ता कि यह कितना लंबा है) स्मृति प्रबंधन, संदर्भ, संकेत, आदि की प्रत्येक बारीकियों को योग कर सकता है क्योंकि 465 लोगों ने इसे वोट किया है, मैं कहूंगा कि यह काफी अच्छा है। जानकारी पर पृष्ठ शुरू। सीखने के लिए और क्या है? ज़रूर, यह कब नहीं है?
लास वी। कार्लसन जूल 26'13

153

मेरी पहली COMP विज्ञान वर्ग में, हमने निम्नलिखित अभ्यास किया। दी, यह एक व्याख्यान कक्ष था जिसमें लगभग 200 छात्र थे ...

प्रोफेसर बोर्ड पर लिखते हैं: int john;

जॉन उठ खड़ा हुआ

प्रोफेसर लिखते हैं: int *sally = &john;

सैली उठ खड़ी होती है, जोहान पर इशारा करती है

प्रोफेसर: int *bill = sally;

बिल जॉन की ओर इशारा करता है

प्रोफेसर: int sam;

सैम खड़ा है

प्रोफेसर: bill = &sam;

बिल अब सैम को इंगित करता है।

मुझे लगता है कि आपको विचार समझ आ गया है। मुझे लगता है कि हमने ऐसा करने में लगभग एक घंटा बिताया, जब तक कि हम पॉइंटर असाइनमेंट की मूल बातें पर नहीं गए।


5
मुझे नहीं लगता कि मुझे यह गलत लगा। मेरा इरादा जॉन से सैम को इंगित-चर के मूल्य को बदलना था। लोगों के साथ प्रतिनिधित्व करना थोड़ा कठिन है, क्योंकि ऐसा लगता है कि आप दोनों बिंदुओं के मूल्य को बदल रहे हैं।
ट्राइक

24
लेकिन कारण यह भ्रमित करने वाला है कि यह ऐसा नहीं है जैसे कि जॉन अपनी सीट से उठ गया और फिर सैम बैठ गया, जैसा कि हम कल्पना कर सकते हैं। यह अधिक है जैसे सैम आया था और जॉन में अपना हाथ चिपका दिया और जॉन के शरीर में सैम प्रोग्रामिंग को क्लोन कर दिया, जैसे मैट्रिक्स में ह्यूगो बुनाई पुनः लोड की गई।
ब्रेटन

59
जैसे सैम जॉन की सीट लेता है, और जॉन कमरे के चारों ओर तब तक तैरता रहता है जब तक कि वह किसी गंभीर चीज से नहीं टकराता और सेगफॉल्ट का कारण बनता है।
just_wes

2
मैं व्यक्तिगत रूप से इस उदाहरण को अनावश्यक रूप से जटिल मानता हूं। मेरे प्रोफेसर ने मुझे एक प्रकाश में इंगित करने के लिए कहा और कहा "आपका हाथ प्रकाश वस्तु का सूचक है"।
सेलेरिटास

इस प्रकार के उदाहरणों के साथ समस्या यह है कि X और X के सूचक एक समान नहीं हैं। और यह लोगों के साथ चित्रित नहीं होता है।
इसहाक नेक्विटेपस

124

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


15
मुझे वास्तव में पसंद है। यह देखना मुश्किल नहीं है कि हाइपरलिंक को दो बार लिखने से दो वेबसाइट दिखाई नहीं देती हैं (ठीक उसी तरह जैसे int *a = bदो प्रतियां नहीं बनातीं *b)।
detly

4
यह वास्तव में बहुत सहज है और सभी को इससे संबंधित होना चाहिए। हालांकि बहुत सारे परिदृश्य हैं जहां यह सादृश्य अलग हो जाता है। एक त्वरित परिचय के लिए महान हालांकि। +1
ब्रायन विग्गिंटन

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

@ThoAppelsin जरूरी नहीं कि सच हो, यदि आप उदाहरण के लिए एक स्थिर HTML वेबपेज का उपयोग कर रहे हैं, तो आप सर्वर पर एक ही फाइल को एक्सेस कर रहे हैं।
17:

5
आप इसे खत्म कर रहे हैं। हाइपरलिंक सर्वर पर मौजूद फाइलों की ओर इशारा करता है, जो कि सादृश्य की सीमा है।
नाटकीय

48

कारण संकेत इतने सारे लोगों को भ्रमित करते हैं कि वे ज्यादातर कंप्यूटर वास्तुकला में बहुत कम या कोई पृष्ठभूमि के साथ आते हैं। चूँकि बहुतों को इस बात का अंदाजा नहीं है कि कंप्यूटर (मशीन) वास्तव में कैसे लागू किया जाता है - C / C ++ में काम करना विदेशी लगता है।

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

साधारण जोड़ से थोड़ा अधिक की आवश्यकता वाले कुछ भी संकेत शामिल करने जा रहे हैं और वे इसे प्राप्त करना सुनिश्चित करते हैं।


2
दिलचस्प। कोई विचार नहीं है कि इस बारे में कैसे शुरू करें। साझा करने के लिए कोई संसाधन?
कारोलिस

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

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

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

4
@ मुझे नहीं लगता कि ऐसे लोगों को समझना आसान है जो केवल संकेत (या, अधिक सटीक, सामान्य रूप से अप्रत्यक्ष) समझ नहीं सकते। आप मूल रूप से यह मान रहे हैं कि जो लोग C में पॉइंटर्स को नहीं समझते हैं वे असेंबली सीखना शुरू कर पाएंगे, कंप्यूटर की अंतर्निहित वास्तुकला को समझ पाएंगे और पॉइंटर्स की समझ के साथ C पर वापस लौटेंगे। यह कई लोगों के लिए सही हो सकता है, लेकिन कुछ अध्ययनों के अनुसार, ऐसा लगता है कि कुछ लोग स्वाभाविक रूप से अप्रत्यक्ष रूप से समझ नहीं सकते हैं, यहां तक ​​कि सिद्धांत रूप में (मुझे अभी भी विश्वास करना बहुत मुश्किल है, लेकिन शायद मैं अपने "छात्रों" के साथ भाग्यशाली रहा हूं ")।
लुआं

27

कई नए, और यहां तक ​​कि पुराने, कॉलेज स्तर के छात्रों के लिए C / C ++ भाषा में भ्रम के प्रमुख कारक क्यों हैं?

एक मूल्य के लिए एक प्लेसहोल्डर की अवधारणा - चर - कुछ हम स्कूल में पढ़ाया जाता है पर नक्शे - बीजगणित। कोई मौजूदा समानांतर नहीं है जिसे आप यह समझे बिना बना सकते हैं कि स्मृति को कंप्यूटर के भीतर कैसे रखा गया है, और कोई भी इस तरह के बारे में नहीं सोचता है जब तक कि वे निम्न स्तर की चीजों से निपट नहीं रहे हैं - सी / सी ++ / बाइट संचार स्तर पर ।

क्या कोई उपकरण या विचार प्रक्रियाएं हैं जो आपको यह समझने में मदद करती हैं कि संकेत चर, फ़ंक्शन और स्तर से परे कैसे काम करते हैं?

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

कुछ अच्छी अभ्यास चीजें क्या हैं जो किसी को "अह-हह, मुझे मिल गया" के स्तर पर लाने के लिए किया जा सकता है, बिना उन्हें समग्र अवधारणा में उलझाए हुए? असल में, परिदृश्य की तरह ड्रिल।

एक ड्रिल के लिए? एक संरचना बनाएं:

struct {
char a;
char b;
char c;
char d;
} mystruct;
mystruct.a = 'r';
mystruct.b = 's';
mystruct.c = 't';
mystruct.d = 'u';

char* my_pointer;
my_pointer = &mystruct.b;
cout << 'Start: my_pointer = ' << *my_pointer << endl;
my_pointer++;
cout << 'After: my_pointer = ' << *my_pointer << endl;
my_pointer = &mystruct.a;
cout << 'Then: my_pointer = ' << *my_pointer << endl;
my_pointer = my_pointer + 3;
cout << 'End: my_pointer = ' << *my_pointer << endl;

उपरोक्त उदाहरण के समान, C को छोड़कर:

// Same example as above, except in C:
struct {
    char a;
    char b;
    char c;
    char d;
} mystruct;

mystruct.a = 'r';
mystruct.b = 's';
mystruct.c = 't';
mystruct.d = 'u';

char* my_pointer;
my_pointer = &mystruct.b;

printf("Start: my_pointer = %c\n", *my_pointer);
my_pointer++;
printf("After: my_pointer = %c\n", *my_pointer);
my_pointer = &mystruct.a;
printf("Then: my_pointer = %c\n", *my_pointer);
my_pointer = my_pointer + 3;
printf("End: my_pointer = %c\n", *my_pointer);

आउटपुट:

Start: my_pointer = s
After: my_pointer = t
Then: my_pointer = r
End: my_pointer = u

शायद उदाहरण के माध्यम से कुछ मूल बातें बताते हैं?


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

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

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

24

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

कोई और पहले से ही इस ट्यूटोरियल से जुड़ा हुआ है, लेकिन मैं उस क्षण को उजागर कर सकता हूं जब मैंने संकेत समझना शुरू किया था:

सी: 3 अध्याय - पॉइंटर्स और स्ट्रिंग्स में पॉइंटर्स और एरर्स पर एक ट्यूटोरियल

int puts(const char *s);

फिलहाल, नजरअंदाज किए गए const.पैरामीटर puts()एक पॉइंटर है, जो कि एक पॉइंटर का मूल्य है (चूंकि C में सभी पैरामीटर्स को वैल्यू द्वारा पास किया जाता है), और पॉइंटर का मूल्य वह पता होता है जिससे यह इंगित करता है, या, बस , एक पता। इस प्रकार जब हम लिखते हैं puts(strA);जैसा हमने देखा है, हम strA [0] का पता दे रहे हैं।

जिस क्षण मैंने इन शब्दों को पढ़ा, बादलों ने भाग लिया और सूर्य के प्रकाश की किरण ने मुझे सूचक समझ के साथ कवर किया।

यहां तक ​​कि अगर आप एक VB .NET या C # डेवलपर हैं (जैसा कि मैं हूं) और असुरक्षित कोड का उपयोग कभी नहीं करता, यह अभी भी समझने योग्य है कि पॉइंटर्स कैसे काम करते हैं, या आप यह नहीं समझ पाएंगे कि ऑब्जेक्ट संदर्भ कैसे काम करते हैं। तब आपके पास सामान्य-लेकिन-गलत धारणा होगी जो ऑब्जेक्ट को किसी विधि के संदर्भ में पास करना ऑब्जेक्ट को कॉपी करता है।


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

@ वोल्फपैक'08 ... क्या ?? आप किस कोड को देख रहे हैं ??
काइल स्ट्रैंड

@KyleStrand मुझे देखने दो।
वोल्फपैक'08

19

मुझे टेड जेन्सेन के "ट्यूटोरियल ऑन पॉइंटर्स एंड एरेज़ेज़ इन सी" मिला जो पॉइंटर्स के बारे में सीखने के लिए एक उत्कृष्ट संसाधन है। यह 10 पाठों में विभाजित है, जो इस बात की व्याख्या से शुरू होता है कि क्या संकेत हैं (और वे किस लिए हैं) और फ़ंक्शन पॉइंटर्स के साथ परिष्करण। http://home.netcom.com/~tjensen/ptr/cpoint.htm

वहाँ से आगे बढ़ते हुए, बीज़ की गाइड टू नेटवर्क प्रोग्रामिंग यूनिक्स सॉकेट्स एपीआई सिखाती है, जिससे आप वास्तव में मजेदार चीजें करना शुरू कर सकते हैं। http://beej.us/guide/bgnet/


1
मैं दूसरे टेड जेन्सेन का ट्यूटोरियल। यह विस्तार के स्तर तक पॉइंटर्स को तोड़ता है, यह अत्यधिक विस्तृत नहीं है, मैंने जो किताब पढ़ी है वह नहीं है। बेहद मददगार! :)
डेव गलाघेर

12

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

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

widget->wazzle.fizzle = fazzle.foozle->wazzle;

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

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

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


10

मैंने सोचा था कि मैं इस सूची में एक सादृश्य जोड़ूंगा कि मुझे कंप्यूटर साइंस ट्यूटर के रूप में संकेत (दिन में वापस) समझाते समय बहुत मदद मिली; पहले, चलो:


चरण सेट करें :

3 स्थानों के साथ पार्किंग स्थल पर विचार करें, ये स्थान गिने हुए हैं:

-------------------
|     |     |     |
|  1  |  2  |  3  |
|     |     |     |

एक तरह से, यह मेमोरी स्थानों की तरह है, वे क्रमबद्ध और संक्रामक हैं .. एक सरणी की तरह। अभी उनमें कोई कार नहीं है इसलिए यह एक खाली सरणी ( parking_lot[3] = {0}) की तरह है।


डेटा जोड़ें

एक पार्किंग स्थल कभी भी लंबे समय तक खाली नहीं रहता है ... अगर यह किया जाता है तो यह बेकार हो जाएगा और कोई भी निर्माण नहीं करेगा। तो मान लीजिए कि दिन 3 कारों, एक नीली कार, एक लाल कार और एक हरे रंग की कार से भर जाता है।

   1     2     3
-------------------
| o=o | o=o | o=o |
| |B| | |R| | |G| |
| o-o | o-o | o-o |

इन कारों सभी एक ही प्रकार के (कार) इसलिए एक तरह से इस के बारे में सोचना है कि हमारी कारें डेटा के कुछ प्रकार (एक का कहना है कि कर रहे हैं int), लेकिन वे अलग-अलग मान ( blue, red, green, कि एक रंग हो सकता है enum)


सूचक दर्ज करें

अब अगर मैं आपको इस पार्किंग स्थल में ले जाऊं, और आपसे एक नीली कार ढूंढने के लिए कहूं, तो आप एक अंगुली का विस्तार करें और उसका उपयोग नीले रंग की कार की ओर करें। ( int *finger = parking_lot)

आपकी उंगली (सूचक) मेरे प्रश्न का उत्तर नहीं है। खोज रहे हैं पर अपनी उंगली मुझे कुछ भी नहीं बताता है, लेकिन अगर मैं देखो जहाँ आप कर रहे हैं उंगली है की ओर इशारा करते (सूचक dereferencing), मैं कार (डेटा) मैं खोज रहा था पा सकते हैं।


सूचक को फिर से सौंपना

अब मैं आपको इसके बदले एक लाल कार खोजने के लिए कह सकता हूं और आप अपनी उंगली को एक नई कार पर पुनर्निर्देशित कर सकते हैं। अब आपका पॉइंटर (पहले जैसा ही) मुझे नए डेटा (पार्किंग स्थल जहां लाल कार मिल सकती है) उसी प्रकार (कार) दिखा रहा है।

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


डबल पॉइंटर्स (या पॉइंटर को पॉइंटर)

यह एक से अधिक पॉइंटर के साथ भी काम करता है। मैं पूछ सकता हूं कि सूचक कहां है, जो लाल कार की ओर इशारा कर रहा है और आप अपने दूसरे हाथ का उपयोग कर सकते हैं और पहली उंगली से उंगली को इंगित कर सकते हैं। (यह ऐसा है int **finger_two = &finger)

अब अगर मैं जानना चाहता हूं कि नीली कार कहां है तो मैं पहली उंगली की दिशा को दूसरी उंगली, कार (डेटा) को फॉलो कर सकता हूं।


झूलने का सूचक

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

   1     2     3
-------------------
| o=o |     | o=o |
| |B| |     | |G| |
| o-o |     | o-o |

आपका सूचक अभी भी इंगित कर रहा है कि लाल कार कहां थी, लेकिन अब नहीं है। मान लीजिए कि एक नई कार वहाँ खींचती है ... एक नारंगी कार। अब अगर मैं आपसे फिर से पूछूं, "लाल कार कहां है", तो आप अभी भी वहीं इशारा कर रहे हैं, लेकिन अब आप गलत हैं। वह लाल रंग की कार नहीं है, वह नारंगी है।


सूचक अंकगणित

ठीक है, इसलिए आप अभी भी दूसरी पार्किंग स्थल पर इशारा कर रहे हैं (अब ऑरेंज कार द्वारा कब्जा कर लिया गया है)

   1     2     3
-------------------
| o=o | o=o | o=o |
| |B| | |O| | |G| |
| o-o | o-o | o-o |

खैर मेरे पास अब एक नया सवाल है ... मैं अगले पार्किंग स्थल में कार का रंग जानना चाहता हूं । आप देख सकते हैं कि आप स्पॉट 2 पर इंगित कर रहे हैं, इसलिए आप केवल 1 जोड़ रहे हैं और आप अगले स्थान पर इंगित कर रहे हैं। ( finger+1), अब चूंकि मैं यह जानना चाहता था कि डेटा क्या था, इसलिए आपको उस स्थान की जांच करनी होगी (न कि केवल उंगली) ताकि आप सूचक को वहां से हटा सकें ( *(finger+1)) यह देखने के लिए कि वहां एक हरे रंग की कार मौजूद है (उस स्थान पर डेटा )


बस "डबल पॉइंटर" शब्द का उपयोग न करें। पॉइंटर्स कुछ भी इंगित कर सकते हैं, इसलिए स्पष्ट रूप से आपके पास अन्य पॉइंटर्स की ओर इशारा करने वाले पॉइंटर्स हो सकते हैं। वे डबल पॉइंटर्स नहीं हैं।
gnasher729

मुझे लगता है कि यह इस बिंदु को याद करता है कि "अंगुलियां" स्वयं, आपकी सादृश्य को जारी रखने के लिए, प्रत्येक "एक पार्किंग स्थल पर कब्जा करती हैं"। मुझे यकीन नहीं है कि लोगों को आपके सादृश्य के उच्च स्तर पर पॉइंटर्स को समझने में कोई कठिनाई हो रही है, यह समझ रहा है कि पॉइंटर्स परस्पर स्थान हैं जो मेमोरी स्थानों पर कब्जा कर लेते हैं, और यह कैसे उपयोगी है, जो लोगों को बाहर निकालना लगता है।
एम्मेट

1
@Emmet - मैं इस बात से असहमत नहीं हूँ कि WRT पॉइंटर्स में और भी बहुत कुछ हो सकता है, लेकिन मैंने सवाल पढ़ा: "without getting them bogged down in the overall concept"एक उच्च स्तरीय समझ के रूप में। और अपनी बात पर: "I'm not sure that people have any difficulty understanding pointers at the high level of abstraction"- आप बहुत आश्चर्यचकित होंगे कि कितने लोग इस स्तर तक भी पॉइंटर्स को नहीं समझते हैं
माइक

क्या किसी व्यक्ति (एक या अधिक उंगलियों के साथ - और एक आनुवांशिक असामान्यता है) में कार-उंगली सादृश्य को विस्तारित करने की कोई योग्यता है जो प्रत्येक को किसी भी दिशा में इंगित करने की अनुमति दे सकता है!) एक अन्य कार (या) की ओर इशारा करते हुए कारों में से एक में बैठ गया बंजर भूमि पर एक "अनइंस्टालिनेटेड पॉइंटर" के रूप में इशारा करते हुए, या एक पूरा हाथ एक "निश्चित आकार [5] बिंदुओं के सरणी" के रूप में रिक्त स्थान की एक पंक्ति पर इंगित करता है या हथेली में "शून्य सूचक" पर मुड़ा हुआ कहीं न कहीं यह इंगित करता है कि यह ज्ञात है कि वहाँ एक कार है) ... 8-)
SlySven

आपकी व्याख्या प्रशंसनीय थी और शुरुआत के लिए एक अच्छी थी।
यतेंद्र राठौर

9

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

कठिनाई, कम से कम जो मैंने अतीत में अनुभव किया है और दूसरों के साथ सौदा देखा है, वह यह है कि सी / सी ++ में संकेत का प्रबंधन अनावश्यक रूप से जटिल हो सकता है।


9

डायग्राम के एक अच्छे सेट के साथ एक ट्यूटोरियल का उदाहरण संकेत की समझ के साथ बहुत मदद करता है

जोएल स्पोलस्की ने अपने गुरिल्ला गाइड टू इंटरव्यू लेख में संकेत को समझने के बारे में कुछ अच्छे बिंदु दिए हैं :

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


8

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

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

जब एक एपीआई कहता है:

int doIt(char *buffer )
//*buffer is a pointer to the buffer

यह क्या चाहता है?

यह चाह सकता है:

एक बफ़र के लिए एक पता का प्रतिनिधित्व करने वाली संख्या

(यह देने के लिए कि, मैं कहता हूं doIt(mybuffer), या doIt(*myBuffer)?)

एक बफ़र को एक पते के पते का प्रतिनिधित्व करने वाली संख्या

(वह है doIt(&mybuffer)या doIt(mybuffer)या doIt(*mybuffer)?)

नंबर को बफ़र के पते से पते का प्रतिनिधित्व करने वाला नंबर

(शायद यह है doIt(&mybuffer)। या यह है doIt(&&mybuffer)? या भी doIt(&&&mybuffer))

और इतने पर, और इसमें शामिल भाषा इसे स्पष्ट नहीं करती है क्योंकि इसमें "पॉइंटर" और "संदर्भ" जैसे शब्द शामिल हैं जो मेरे लिए उतना अर्थ और स्पष्टता नहीं रखते हैं जितना कि "x का पता y" और "है। इस फ़ंक्शन को y के लिए एक पते की आवश्यकता है "। इसके अतिरिक्त उत्तर केवल इस बात पर निर्भर करता है कि बिल्ली "माइबफ़र" किसके साथ शुरू होती है, और इसके साथ क्या करना है। भाषा उन घोंसलों के स्तरों का समर्थन नहीं करती है जो व्यवहार में सामने आते हैं। जैसे जब मुझे किसी नए बफर को बनाने वाले फ़ंक्शन में "पॉइंटर" को सौंपना होता है, और यह पॉइंटर को बफर के नए स्थान पर इंगित करने के लिए संशोधित करता है। क्या यह वास्तव में पॉइंटर, या पॉइंटर को पॉइंटर करना चाहता है, इसलिए यह जानता है कि पॉइंटर की सामग्री को संशोधित करने के लिए कहां जाना है। ज्यादातर समय मुझे सिर्फ अनुमान लगाना होता है कि इसका मतलब क्या है "

"पॉइंटर" बस अतिभारित है। क्या एक सूचक एक मान का पता है? या यह एक वैरिएबल है जो एक मान को एक पता रखता है। जब कोई फ़ंक्शन पॉइंटर चाहता है, तो क्या वह पता चाहता है जो पॉइंटर वैरिएबल रखता है, या क्या वह पॉइंटर वैरिएबल का पता चाहता है? मैं उलझन में हूं।


मैंने इसे इस तरह समझाया है: यदि आप एक सूचक घोषणा की तरह देखते हैं double *(*(*fn)(int))(char), तो मूल्यांकन का परिणाम *(*(*fn)(42))('x')होगा double। आप यह समझने के लिए मूल्यांकन की परतों को अलग कर सकते हैं कि मध्यवर्ती प्रकार क्या होना चाहिए।
बेरंड जेन्ड्रीसेक

@BerndJendrissek मुझे यकीन नहीं है। (*(*fn)(42))('x') फिर मूल्यांकन करने का परिणाम क्या है ?
ब्रेटन

आपको एक चीज़ मिलती है (इसे कॉल करें x), यदि आप मूल्यांकन करते हैं *x, तो आपको एक डबल मिलता है।
बेरंड जेन्ड्रीसेक

@BerndJendrissek यह संकेत के बारे में कुछ समझाने वाला है? मुझे नहीं मिला। अापका नजरिया क्या है? मैंने एक परत छीन ली है, और किसी भी मध्यवर्ती प्रकार के बारे में कोई नई जानकारी नहीं प्राप्त की है। यह इस बारे में क्या समझाता है कि कोई विशेष कार्य क्या स्वीकार करने जा रहा है? इसका किसी चीज से क्या लेना-देना है?
ब्रेटन

हो सकता है कि इस विवरण में संदेश (और यह मेरा नहीं है, मुझे लगता है मैं मिल सकता है इच्छा जहां मैं पहली बार देखा यह) क्या के मामले में कम इसके बारे में सोचने के लिए है fn है और अधिक आप क्या कर सकते हैं के संदर्भ में कर के साथfn
बर्न्ड Jendrissek

8

मुझे लगता है कि संकेत को समझने में मुख्य बाधा बुरे शिक्षक हैं।

लगभग सभी को संकेत के बारे में झूठ सिखाया जाता है: कि वे स्मृति पतों से अधिक कुछ नहीं हैं , या कि वे आपको मनमाने स्थानों पर इंगित करने की अनुमति देते हैं ।

और निश्चित रूप से वे समझना मुश्किल है, खतरनाक और अर्ध-जादुई।

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

मैंने इस ब्लॉग पोस्ट में कुछ महीने पहले इसका स्पष्टीकरण लिखने की कोशिश की - उम्मीद है कि यह किसी की मदद करेगा।

(ध्यान दें, इससे पहले कि किसी को भी मुझ पर पंडिताऊ हो जाता है, हाँ, सी ++ मानक कहता है कि संकेत का प्रतिनिधित्व स्मृति पतों। लेकिन यह कहना नहीं है कि "संकेत स्मृति पते, और कुछ नहीं बल्कि स्मृति पते हैं और इस्तेमाल किया जा सकता या स्मृति के साथ एक दूसरे के स्थान के बारे में सोचा पते "। भेद महत्वपूर्ण है)


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

जरुरी नहीं। आपको संकेत देने के लिए अपने सिर में कंप्यूटर को मॉडल करने में सक्षम होना चाहिए (और साथ ही अन्य कार्यक्रमों को डीबग करना)। हर कोई ऐसा नहीं कर सकता।
थोर्बोजर्न रेवन एंडरसन

5

मुझे लगता है कि जो बात सीखने के लिए इशारा करती है, वह यह है कि जब तक कि आप इस विचार के साथ सहज नहीं हो जाते हैं कि "इस मेमोरी लोकेशन में बिट्स का एक सेट है जो एक इंट, एक डबल, एक कैरेक्टर, जो भी" का प्रतिनिधित्व करता है।

जब आप पहली बार एक पॉइंटर देखते हैं, तो आपको वास्तव में उस मेमोरी लोकेशन पर नहीं मिलता है। "आपका क्या मतलब है, यह एक पता रखता है ?"

मैं इस धारणा से सहमत नहीं हूं कि "आप या तो उन्हें प्राप्त करते हैं या आप नहीं"।

उन्हें समझना आसान हो जाता है जब आप उनके लिए वास्तविक उपयोग ढूंढना शुरू करते हैं (जैसे कार्यों में बड़ी संरचनाओं को पारित नहीं करना)।


5

यह समझने में कठिन होने का कारण यह नहीं है कि यह एक कठिन अवधारणा है, बल्कि इसलिए कि वाक्य रचना असंगत है

   int *mypointer;

आपको पहली बार पता चला है कि एक चर रचना का बायाँ हिस्सा चर के प्रकार को परिभाषित करता है। सूचक घोषणा सी और सी ++ में इस तरह से काम नहीं करता है। इसके बजाय वे कहते हैं कि चर बाईं ओर के प्रकार पर इंगित कर रहा है। इस मामले में: *mypointer एक int पर इंगित कर रहा है।

जब तक मैं उन्हें C # (असुरक्षित के साथ) उपयोग करने की कोशिश नहीं करता, तब तक वे पूरी तरह से संकेत नहीं देते थे, वे ठीक उसी तरह से काम करते हैं लेकिन तार्किक और सुसंगत वाक्य रचना के साथ। सूचक स्वयं एक प्रकार है। यहाँ mypointer है किसी पूर्णांक पर एक सूचक।

  int* mypointer;

यहां तक ​​कि मुझे फंक्शन पॉइंटर्स पर शुरू करने की ज़रूरत नहीं है ...


2
दरअसल, आपके दोनों खंड सी मान्य हैं। यह बहुत सी वर्षों की शैली का मामला है कि पहला आम अधिक आम है। उदाहरण के लिए C ++ में दूसरा काफी अधिक सामान्य है।
RBerteig

1
दूसरा टुकड़ा वास्तव में अधिक जटिल घोषणाओं के साथ अच्छी तरह से काम नहीं करता है। और सिंटैक्स "असंगत" के रूप में नहीं है एक बार जब आप महसूस करते हैं कि एक पॉइंटर घोषणा का दाहिना हाथ आपको दिखाता है कि आपको पॉइंटर के लिए क्या करना चाहिए ताकि कुछ ऐसा हो सके जिसका प्रकार बाईं ओर परमाणु प्रकार निर्दिष्ट है।
बेरंड जेन्ड्रीसेक

2
int *p;एक सरल अर्थ है: *pएक पूर्णांक है। int *p, **ppका अर्थ है: *pऔर **ppपूर्णांक हैं।
माइल्स राउत

@ माइल्सआउट: लेकिन यह वास्तव में समस्या है। *pऔर **ppकर रहे हैं नहीं पूर्णांकों, क्योंकि आप initialised कभी नहीं pया ppया *ppकुछ भी करने के लिए बात करने के लिए। मैं समझता हूं कि कुछ लोग इस पर व्याकरण के साथ चिपके रहना पसंद करते हैं, विशेष रूप से क्योंकि कुछ किनारे के मामलों और जटिल मामलों में आपको ऐसा करने की आवश्यकता होती है (हालांकि, अभी भी, आप तुच्छ रूप से चारों ओर काम कर सकते हैं जो सभी मामलों में मुझे पता है) ... लेकिन मुझे नहीं लगता कि ये मामले इस तथ्य से अधिक महत्वपूर्ण हैं कि सही-संरेखण सिखाना न्यूबाय्स को भ्रमित करना है। बदसूरत की तरह उल्लेख करने के लिए नहीं! :)
कक्षा

@LightnessRacesinOrbit शिक्षण सही संरेखण भ्रामक से दूर है। इसे पढ़ाने का एकमात्र सही तरीका है। यह नहीं सिखा रहा है कि यह भ्रामक है।
माइल्स रुट

5

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


4

मुझे घर का पता सादृश्य पसंद है, लेकिन मुझे हमेशा पता है कि यह पता मेलबॉक्स के लिए ही है। इस तरह आप पॉइंटर को खोलने (मेलबॉक्स को खोलने) की अवधारणा की कल्पना कर सकते हैं।

उदाहरण के लिए एक लिंक की गई सूची के बाद: 1) पते के साथ अपने पेपर की शुरुआत करें 2) कागज पर दिए पते पर जाएं 3) उस पर अगले पते के साथ कागज का एक नया टुकड़ा खोजने के लिए मेलबॉक्स खोलें।

एक रैखिक लिंक्ड सूची में, अंतिम मेलबॉक्स में इसमें कुछ भी नहीं है (सूची का अंत)। एक गोलाकार लिंक्ड सूची में, अंतिम मेलबॉक्स में पहले मेलबॉक्स का पता है।

ध्यान दें कि चरण 3 वह जगह है जहां पर स्थगन होता है और जहां पता अमान्य होने पर आप दुर्घटनाग्रस्त हो जाएंगे या गलत हो जाएंगे। यह मानते हुए कि आप एक अमान्य पते के मेलबॉक्स तक चल सकते हैं, कल्पना करें कि एक ब्लैक होल है या वहां कुछ ऐसा है जो दुनिया को अंदर ले जाता है :)


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

3

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

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


2

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


2

मुझे लगता है कि यह वास्तव में एक वाक्यविन्यास मुद्दा हो सकता है। बिंदुओं के लिए C / C ++ सिंटैक्स असंगत और अधिक जटिल लगता है जितना कि यह होना चाहिए।

विडंबना यह है कि जिस चीज़ ने मुझे वास्तव में पॉइंटर्स को समझने में मदद की, वह सी ++ मानक टेम्पलेट लाइब्रेरी में एक पुनरावृत्त की अवधारणा का सामना कर रही थी । यह विडंबना है क्योंकि मैं केवल यह मान सकता हूं कि पुनरावृत्तियों को सूचक के सामान्यीकरण के रूप में कल्पना की गई थी।

कभी-कभी आप जंगल को तब तक नहीं देख सकते जब तक आप पेड़ों को अनदेखा करना नहीं सीखते।


समस्या ज्यादातर सी घोषणा सिंटैक्स में है। लेकिन पॉइंटर का उपयोग निश्चित रूप से आसान (*p)होगा यदि यह होता (p->), और इस प्रकार हम p->->xअस्पष्ट के बजाय होता*p->x
MSalters

@MSalters ओह मेरे भगवान आप मजाक कर रहे हैं, है ना? वहां कोई विसंगतियां नहीं हैं। a->bबस मतलब है (*a).b
माइल्स रुट

@ मीलों: वास्तव में, और उस तर्क से * p->xमतलब है * ((*a).b)जबकि *p -> xसाधन (*(*p)) -> x। प्रीफ़िक्स और पोस्टफ़िक्स ऑपरेटरों को मिलाकर अस्पष्ट पार्सिंग का कारण बनता है।
MSalters

@MSalters नहीं, क्योंकि व्हाट्सएप अप्रासंगिक है। यह कहने की तरह है कि 1+2 * 39. होना चाहिए
मील्स राउत

2

भ्रम "सूचक" अवधारणा में एक साथ मिश्रित कई अमूर्त परतों से आता है। प्रोग्रामर जावा / पायथन में सामान्य संदर्भों से भ्रमित नहीं होते हैं, लेकिन संकेत इस मायने में अलग हैं कि वे अंतर्निहित मेमोरी-आर्किटेक्चर की विशेषताओं को उजागर करते हैं।

अमूर्तता की अलग-अलग परतों को साफ करना एक अच्छा सिद्धांत है, और संकेत ऐसा नहीं करते हैं।


1
दिलचस्प बात यह है, कि सी पॉइंटर्स वास्तव में अंतर्निहित मेमोरी आर्किटेक्चर के किसी भी वर्णव्यवस्था को उजागर नहीं करते हैं। जावा संदर्भ और सी पॉइंटर्स के बीच एकमात्र अंतर यह है कि आप जटिल प्रकार के पॉइंटर्स शामिल कर सकते हैं (जैसे। इंट *** या चार * ( ) (शून्य * )), एरियर्स और स्ट्रक्चर सदस्यों के लिए पॉइंटर्स के लिए सूचक अंकगणित है, उपस्थिति शून्य की *, और सरणी / सूचक द्वंद्व। इसके अलावा, वे सिर्फ एक ही काम करते हैं।
jpalecek

अच्छी बात। यह सूचक अंकगणितीय है और बफर ओवरफ्लो की संभावना है - वर्तमान में प्रासंगिक मेमोरी क्षेत्र से बाहर निकलकर एब्सट्रैक्शन को तोड़ना - जो ऐसा करता है।
जोशुआ फॉक्स

@jpalecek: यह समझना बहुत आसान है कि पॉइंटर्स कार्यान्वयन पर कैसे काम करते हैं जो अंतर्निहित वास्तुकला के संदर्भ में उनके व्यवहार को दस्तावेज करते हैं। कहने का foo[i]मतलब है कि एक निश्चित स्थान पर जाना, एक निश्चित दूरी को आगे बढ़ाना, और यह देखना कि वहां क्या है। जो चीजें उलझी रहती हैं, वह अधिक जटिल अतिरिक्त अमूर्त परत होती है, जिसे मानक रूप से संकलक के लाभ के लिए जोड़ा गया था, लेकिन चीजों को इस तरह से मॉडल करना कि प्रोग्रामर की जरूरतों के लिए खराब फिट हो और संकलक को समान रूप से आवश्यकता हो।
सुपरकैट

2

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

इसलिए मैं कहता हूं कि राम एक सरणी है (और आपके पास केवल 10-बाइट्स रैम है):

unsigned char RAM[10] = { 10, 14, 4, 3, 2, 1, 20, 19, 50, 9 };

तब एक चर के लिए एक सूचक वास्तव में रैम में उस चर का सूचकांक (पहला बाइट) है।

इसलिए यदि आपके पास एक पॉइंटर / इंडेक्स है unsigned char index = 2, तो मूल्य स्पष्ट रूप से तीसरा तत्व है, या संख्या 4. एक पॉइंटर को एक पॉइंटर है जहां आप उस नंबर को लेते हैं और इसे एक इंडेक्स के रूप में उपयोग करते हैं, जैसे RAM[RAM[index]]

मैं कागज की एक सूची पर एक सरणी आकर्षित करूंगा, और बस इसका उपयोग कई बिंदुओं जैसे एक ही मेमोरी, पॉइंटर अंकगणितीय, पॉइंटर टू पॉइंटर, और इसी तरह से इंगित करने के लिए चीजों को दिखाने के लिए करूंगा।


1

पोस्ट ऑफिस बॉक्स नंबर।

यह जानकारी का एक टुकड़ा है जो आपको कुछ और उपयोग करने की अनुमति देता है।

(और यदि आप डाकघर के बॉक्स नंबरों पर अंकगणित करते हैं, तो आपको समस्या हो सकती है, क्योंकि पत्र गलत बॉक्स में चला जाता है। और यदि कोई दूसरे राज्य में जाता है - कोई अग्रेषण पता नहीं है - तो आपके पास झूलने वाला सूचक है।) दूसरी ओर - यदि डाक घर मेल को आगे बढ़ाता है, तो आपके पास एक संकेतक के लिए एक संकेतक है।)


1

इट्रैटर्स के माध्यम से, इसे समझने का कोई बुरा तरीका नहीं है .. लेकिन आप यह देखते रहेंगे कि अलेक्जेंड्रेस्कु उनके बारे में शिकायत करना शुरू कर देगा।

कई पूर्व-सी ++ देव (जो कभी नहीं समझ पाए कि भाषा को डंप करने से पहले पुनरावृत्तियां एक आधुनिक सूचक हैं) सी # पर कूदें और अभी भी मानते हैं कि उनके पास सभ्य चलने वाले हैं।

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

अविवेक।

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

IEnumerable + LINQ + बड़े पैमाने पर फ्रेमवर्क = 300MB रनटाइम पेनल्टी का अप्रत्यक्ष उपयोग, संदर्भ प्रकारों के उदाहरणों के ढेर के माध्यम से ऐप्स को खींचना ।।

"ले पॉइंटर सस्ता है।"


4
किसी भी चीज़ से इसका क्या लेना - देना है?
नील विलियम्स

... आप क्या कहना चाह रहे हैं, इसके अलावा "स्टैटिक लिंकिंग अब तक की सबसे अच्छी बात है" और "मुझे समझ नहीं आ रहा है कि जो मैंने पहले काम किया है, उससे अलग कुछ भी नहीं"?
लुआं

Luaan, आप संभवतः यह नहीं जान सकते कि 2000 में JIT से असहमति करके कोई क्या सीख सकता है? यह एक तालिका में एक पॉइंटर टेबल से समाप्त होता है, जैसा कि 2000 में एएसएम में ऑनलाइन दिखाया गया है, इसलिए कुछ भी अलग नहीं समझना एक और अर्थ ले सकता है: ध्यान से पढ़ना एक आवश्यक कौशल है, इसे फिर से आज़माएं।
राम-जका टोटी

1

ऊपर दिए गए कुछ जवाबों ने कहा है कि "संकेत वास्तव में कठिन नहीं हैं", लेकिन सीधे उस पते पर नहीं गए जहां "सूचक कठिन हैं!" से आता है। कुछ साल पहले मैंने पहले वर्ष के सीएस छात्रों (केवल एक वर्ष के लिए, क्योंकि मैं स्पष्ट रूप से इसे चूसा था) के बारे में पढ़ा था और मेरे लिए यह स्पष्ट था कि सूचक का विचार कठिन नहीं है। यह समझना मुश्किल है कि आप सूचक को क्यों और कब चाहते हैं

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


0

मैं यह नहीं देखता कि संकेत देने वालों के बारे में क्या भ्रम है। वे स्मृति में एक स्थान को इंगित करते हैं, वह यह है कि स्मृति पते को संग्रहीत करता है। C / C ++ में आप पॉइंटर पॉइंट्स को टाइप करने के लिए निर्दिष्ट कर सकते हैं। उदाहरण के लिए:

int* my_int_pointer;

कहते हैं कि my_int_pointer में किसी ऐसे स्थान का पता होता है जिसमें एक int होता है।

पॉइंटर्स के साथ समस्या यह है कि वे मेमोरी में एक स्थान को इंगित करते हैं, इसलिए कुछ ऐसे स्थान पर ट्रेस करना आसान है जो आप में नहीं हैं। सबूत के रूप में बफर ओवरफ्लो से सी / सी ++ अनुप्रयोगों में कई सुरक्षा छेदों को देखें (पॉइंटर को बढ़ाते हुए)। आवंटित सीमा से पहले)।


0

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

http://arjay.bc.ca/Modula-2/Text/Ch15/Ch15.8.html#15.8.5 मेरे बारे में इससे कहीं अधिक सुसंगत बात करता है। :-)


-1: हैंडल पॉइंटर्स टू पॉइंटर्स नहीं हैं; वे किसी भी मायने में संकेत नहीं हैं। उन्हें भ्रमित मत करो।
इयान गोल्डबी

"वे किसी भी मायने में संकेत नहीं हैं" - उम, मैं अलग करने के लिए भीख माँगता हूँ।
SarekOfVulcan

एक पॉइंटर एक मेमोरी लोकेशन है। एक हैंडल किसी भी विशिष्ट पहचानकर्ता है। यह एक पॉइंटर हो सकता है, लेकिन यह सिर्फ एक सरणी में एक इंडेक्स हो सकता है, या उस मामले के लिए कुछ और भी हो सकता है। आपके द्वारा दिया गया लिंक सिर्फ एक विशेष मामला है जहां हैंडल एक पॉइंटर है, लेकिन यह होना जरूरी नहीं है। यह भी देखें parashift.com/c++-faq-lite/references.html#faq-8.8
इयान गोल्डबी

यह लिंक आपके दावे का समर्थन नहीं करता है कि वे किसी भी अर्थ में संकेत नहीं कर रहे हैं, या तो - "उदाहरण के लिए, हैंडल फ्रेड ** हो सकते हैं, जहां पॉइंट-टू फ्रेड * पॉइंटर्स ..." मुझे नहीं लगता -1 मेला था।
सरेकऑफवल्कैन

0

हर C / C ++ शुरुआत करने वाले को एक ही समस्या होती है और यह समस्या इसलिए नहीं होती है क्योंकि "संकेत सीखने में कठिन होते हैं" लेकिन "किसे और कैसे समझाया जाता है"। कुछ शिक्षार्थी इसे मौखिक रूप से इकट्ठा करते हैं कुछ नेत्रहीन और इसे समझाने का सबसे अच्छा तरीका "ट्रेन" उदाहरण (मौखिक और दृश्य उदाहरण के लिए सूट) का उपयोग करना है।

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

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