एक शुरुआत करने के लिए सी पॉइंटर्स (घोषणा बनाम बनाम ऑपरेटर) कैसे समझा जाए?


141

मुझे हाल ही में एक सी प्रोग्रामिंग शुरुआत की ओर इशारा करने वाले लोगों को समझाने में खुशी हुई है और निम्नलिखित कठिनाई पर ठोकर खाई है। यह एक समस्या की तरह प्रतीत नहीं हो सकता है यदि आप पहले से ही संकेत का उपयोग करना जानते हैं, लेकिन एक स्पष्ट उदाहरण के साथ निम्नलिखित उदाहरण को देखने की कोशिश करें:

int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);

निरपेक्ष शुरुआत करने के लिए आउटपुट आश्चर्यजनक हो सकता है। पंक्ति 2 में उसने सिर्फ * बार को & फू होने की घोषणा की थी, लेकिन पंक्ति 4 में यह पता चलता है कि बार वास्तव में & foo के बजाय फू है!

भ्रम, आप कह सकते हैं, * प्रतीक की अस्पष्टता से उपजा है: पंक्ति 2 में इसका उपयोग सूचक को घोषित करने के लिए किया जाता है। पंक्ति 4 में इसका उपयोग एक अपर संचालक के रूप में किया जाता है जो सूचक बिंदुओं पर मान प्राप्त करता है। दो अलग बातें, है ना?

हालाँकि, यह "स्पष्टीकरण" एक शुरुआत में मदद नहीं करता है। यह एक सूक्ष्म विसंगति को इंगित करके एक नई अवधारणा का परिचय देता है। यह इसे सिखाने का सही तरीका नहीं हो सकता।

तो, कार्निघन और रिची ने इसे कैसे समझाया?

यूनिरी ऑपरेटर * अप्रत्यक्ष या डीफ्रेन्सिंग ऑपरेटर है; जब एक पॉइंटर पर लागू किया जाता है, तो यह पॉइंटर पॉइंटर को ऑब्जेक्ट को एक्सेस करता है। [...]

सूचक आईपी की घोषणा, int *ipएक महामारी के रूप में इरादा है; यह कहता है कि अभिव्यक्ति *ipएक इंट है। एक चर के लिए घोषणा का वाक्यविन्यास अभिव्यक्तियों का वाक्यविन्यास नकल करता है जिसमें चर दिखाई दे सकता है

int *ipपढ़ा जाना चाहिए जैसे " *ipलौट आएंगे int"? लेकिन घोषणा के बाद उस पैटर्न का पालन क्यों नहीं किया जाता है? क्या होगा अगर एक शुरुआती चर को शुरू करना चाहता है? int *ip = 1(पढ़ें: *ipवापसी करेगा intऔर intहै 1) उम्मीद के मुताबिक काम नहीं करेगा। वैचारिक मॉडल सिर्फ सुसंगत नहीं लगता है। क्या मुझसे कोई चूक हो रही है?


संपादित करें: इसने यहां उत्तरों को संक्षेप में प्रस्तुत करने का प्रयास किया


15
सबसे अच्छा स्पष्टीकरण चीजों को एक कागज़ पर चित्रित करके और उन्हें तीरों से जोड़ना है;)
Maroun

16
जब मुझे पॉइंटर्स सिंटैक्स की व्याख्या करनी थी, तो मैंने हमेशा इस तथ्य पर जोर दिया कि *एक घोषणा में एक टोकन अर्थ है "एक पॉइंटर घोषित करें", अभिव्यक्ति में यह अनुमेय ऑपरेटर है, और ये दोनों अलग-अलग चीजों का प्रतिनिधित्व करते हैं जो एक ही प्रतीक के लिए होते हैं। (समान गुणक ऑपरेटर के समान - एक ही प्रतीक, अलग अर्थ)। यह भ्रामक है, लेकिन मामलों की वास्तविक स्थिति से अलग कुछ और भी बदतर होने जा रहा है।
मट्टियो इटालिया

40
शायद इसे लिखने से यह int* barस्पष्ट हो जाता है कि तारा वास्तव में प्रकार का हिस्सा है, पहचानकर्ता का हिस्सा नहीं है। बेशक यह आपको अलग-अलग समस्याओं में चलाता है जैसे कि अनजाने सामान int* a, b
निकल्स बी।

9
मैंने हमेशा सोचा है कि K & R स्पष्टीकरण मूर्खतापूर्ण और अनावश्यक है। भाषा दो अलग-अलग चीजों के लिए एक ही प्रतीक का उपयोग करती है और हमें बस इससे निपटना है। *संदर्भ के आधार पर दो अलग-अलग अर्थ हो सकते हैं। जैसे एक ही अक्षर का अलग-अलग तरह से उच्चारण किया जा सकता है, यह उस शब्द पर निर्भर करता है, जिसमें कई भाषाओं को बोलना सीखना मुश्किल हो जाता है। यदि हर एक अवधारणा / ऑपरेशन का अपना प्रतीक होता है तो हमें बहुत बड़े कीबोर्ड की आवश्यकता होती है, इसलिए जब ऐसा करने के लिए प्रतीक को पुनर्नवीनीकरण किया जाता है।
आर्ट

8
मैं एक ही समस्या में कई बार भागा हूं जब सी को दूसरों को पढ़ा रहा हूं और, मेरे अनुभव में, यह उस तरह से हल किया जा सकता है जैसे यहां ज्यादातर लोगों ने सुझाव दिया है। सबसे पहले, सी सिंटैक्स के बिना एक पॉइंटर की अवधारणा को समझाएं। फिर, सिंटैक्स सिखाएं और तार के हिस्से के रूप में तारांकन पर जोर दें ( int* p), अपने छात्र को एक ही पंक्ति में कई घोषणाओं का उपयोग करने के खिलाफ चेतावनी देते हुए जब संकेत शामिल होते हैं। जब छात्र बिंदुओं की अवधारणा को पूरी तरह से समझ गया है, तो छात्र को समझाएं कि int *pवाक्यविन्यास समान है और फिर कई घोषणाओं के साथ समस्या को समझाएं।
थियोडोरोस चट्जीगनिनाकिस

जवाबों:


43

आपके छात्र को *विभिन्न संदर्भों में प्रतीक का अर्थ समझने के लिए , उन्हें पहले समझना चाहिए कि संदर्भ वास्तव में अलग हैं। एक बार जब वे समझते हैं कि संदर्भ अलग-अलग हैं (यानी एक असाइनमेंट के बाएं हाथ की तरफ और एक सामान्य अभिव्यक्ति के बीच अंतर) यह समझने के लिए बहुत अधिक संज्ञानात्मक छलांग नहीं है कि अंतर क्या हैं।

सबसे पहले यह समझाएं कि एक चर की घोषणा में ऑपरेटर नहीं हो सकते हैं (यह दिखाते हुए कि यह एक डाल को प्रदर्शित करता है -+ एक चर घोषणा में प्रतीक या चिह्न त्रुटि होती है)। फिर यह दिखाने के लिए जाएं कि एक अभिव्यक्ति (यानी एक असाइनमेंट के दाहिने हाथ की तरफ) में ऑपरेटर हो सकते हैं। सुनिश्चित करें कि छात्र समझता है कि एक अभिव्यक्ति और एक चर घोषणा दो पूरी तरह से अलग संदर्भ हैं।

जब वे समझते हैं कि संदर्भ अलग हैं, तो आप यह समझाने के लिए जा सकते हैं कि कब * प्रतीक चर पहचानकर्ता के सामने एक चर घोषणा में है, तो इसका मतलब है कि 'इस चर को सूचक के रूप में घोषित करें'। तब आप यह समझा सकते हैं कि जब एक अभिव्यक्ति (एक अपर संचालक के रूप में) का उपयोग किया जाता है तो *प्रतीक 'डीरेफेरेंस ऑपरेटर' होता है और इसका अर्थ है 'अपने पहले के अर्थ के बजाय' के पते पर मूल्य।

अपने छात्र को वास्तव में समझाने के लिए, यह समझाइए कि C के निर्माता किसी भी प्रतीक का इस्तेमाल कर सकते हैं, जिसका अर्थ है कि डिरेफेरेंस ऑपरेटर (यानी वे @इसके बजाय इसका इस्तेमाल कर सकते थे ) लेकिन जिस भी कारण से उन्होंने डिज़ाइन का उपयोग करने का निर्णय लिया*

सब के सब, वहाँ कोई रास्ता नहीं है कि यह समझाने के आसपास है कि संदर्भ अलग हैं। यदि छात्र को समझ नहीं आता है कि संदर्भ अलग हैं, तो वे यह नहीं समझ *सकते कि प्रतीक का मतलब अलग-अलग चीजों से क्यों हो सकता है।


80

शॉर्टहैंड का कारण:

int *bar = &foo;

आपके उदाहरण में भ्रामक यह हो सकता है कि इसके बराबर होने के नाते इसे गलत तरीके से फैलाना आसान है:

int *bar;
*bar = &foo;    // error: use of uninitialized pointer bar!

जब वास्तव में इसका मतलब होता है:

int *bar;
bar = &foo;

इस तरह से लिखा गया है, चर घोषणा और असाइनमेंट अलग होने के साथ, भ्रम की कोई संभावना नहीं है, और आपके K & R उद्धरण में वर्णित, घोषणा समानता का उपयोग पूरी तरह से काम करता है:

  • पहली पंक्ति एक चर वाणी bar, ऐसी है कि *barएक int

  • दूसरी पंक्ति का पता प्रदान करती है fooकरने के लिए bar, बनाने *bar(एक int) के लिए एक उपनाम foo(यह भी एक int)।

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


4
मुझे लुभाया जाएगा typedeftypedef int *p_int;इसका मतलब है कि प्रकार के एक चर में p_intसंपत्ति है *p_intजो एक है int। फिर हमारे पास है p_int bar = &foo;। किसी को भी अनइंस्टॉल किए गए डेटा बनाने के लिए प्रोत्साहित करना और बाद में इसे डिफ़ॉल्ट आदत के रूप में असाइन करना ... एक बुरे विचार की तरह लगता है।
यक्क - एडम नेवरामॉन्ट

6
यह केवल सी-घोषणाओं की मस्तिष्क-क्षतिग्रस्त शैली है; यह संकेत करने के लिए विशिष्ट नहीं है। विचार करें int a[2] = {47,11};, यह (गैर-मौजूद) तत्व a[2]एहीर का एक प्रारंभिककरण नहीं है ।
मार्क वैन लीउवेन

5
@MarcvanLeeuwen मस्तिष्क-क्षति से सहमत हैं। आदर्श रूप से, *प्रकार का हिस्सा होना चाहिए, चर के लिए बाध्य नहीं है, और फिर आप int* foo_ptr, bar_ptrदो बिंदुओं को घोषित करने के लिए लिखने में सक्षम होंगे । लेकिन यह वास्तव में एक सूचक और पूर्णांक घोषित करता है।
बरमार

1
यह केवल "शॉर्टहैंड" घोषणाओं / असाइनमेंट के बारे में नहीं है। पूरा मुद्दा फिर से उस क्षण आता है जब आप फ़ंक्शन तर्क के रूप में संकेत का उपयोग करना चाहते हैं।
अरमान

30

घोषणाओं पर संक्षिप्त

घोषणा और आरंभीकरण के बीच का अंतर जानना अच्छा है। हम चर को प्रकार के रूप में घोषित करते हैं और उन्हें मूल्यों के साथ आरंभ करते हैं। यदि हम दोनों एक ही समय में करते हैं तो हम अक्सर इसे एक परिभाषा कहते हैं।

1. int a; a = 42;

int a;
a = 42;

हम एक नाम घोषित करते हैं a । फिर हम इसे एक मूल्य देकर आरंभ करते हैं ।int42

2. int a = 42;

हम घोषणा करते हैं और एकint नाम देते हैं और इसे 42 मूल्य देते हैं। इसके साथ आरंभ किया जाता है । परिभाषा।42

3. a = 43;

जब हम चर का उपयोग करते हैं तो हम कहते हैं कि हम उन पर काम करते हैं।a = 43एक असाइनमेंट ऑपरेशन है। हम वेरिएबल a के लिए 43 नंबर असाइन करते हैं।

कहने से

int *bar;

हम बार को एक संकेतक का सूचक घोषित करते हैं । कहने से

int *bar = &foo;

हम बार की घोषणा करते हैं और इसे फू के पते से शुरू करते हैं

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

इसके अलावा मैंने तस्वीर को बोलने दिया।

क्या

क्या हो रहा है इस पर एक सरलीकृत वर्गीकरण। (और यहां एक खिलाड़ी संस्करण अगर आप रोकना चाहते हैं आदि)

          ASCIIMATION


22

2 कथन int *bar = &foo;को स्मृति में सचित्र रूप में देखा जा सकता है,

   bar           foo
  +-----+      +-----+
  |0x100| ---> |  1  |
  +-----+      +-----+ 
   0x200        0x100

अब barएक प्रकार intका सूचक है जिसमें पता &है foo। यूनिरी ऑपरेटर का उपयोग करके *हम पॉइंटर का उपयोग करके 'फू' में निहित मूल्य को पुनः प्राप्त करने के लिए टाल देते हैं bar

संपादित करें : शुरुआती लोगों के साथ मेरा दृष्टिकोण memory addressएक चर की व्याख्या करना है

Memory Address:प्रत्येक चर में ओएस द्वारा प्रदान किया गया एक संबोधन होता है। में int a;, &aचर का पता है a

बुनियादी प्रकार के चर को इस प्रकार समझाना जारी रखें C,

Types of variables: चर संबंधित प्रकारों के मान रख सकते हैं लेकिन पते नहीं।

int a = 10; float b = 10.8; char ch = 'c'; `a, b, c` are variables. 

Introducing pointers: उदाहरण के लिए, चर के ऊपर कहा गया है

 int a = 10; // a contains value 10
 int b; 
 b = &a;      // ERROR

यह असाइन करना संभव है , b = aलेकिन नहीं b = &a, क्योंकि चर bमान पकड़ सकता है, लेकिन पता नहीं, इसलिए हमें पॉइंटर्स की आवश्यकता होती है ।

Pointer or Pointer variables :यदि एक चर में एक पता होता है तो इसे एक सूचक चर के रूप में जाना जाता है। *घोषणा में उपयोग करें कि यह एक सूचक है।

 Pointer can hold address but not value
 Pointer contains the address of an existing variable.
 Pointer points to an existing variable

3
समस्या यह है कि int *ip"आईपी एक प्रकार का सूचक (*) प्रकार का इंट" है जब आप कुछ पढ़ते समय परेशानी में पड़ जाते हैं x = (int) *ip
आर्मिन

2
@abw यह पूरी तरह से अलग है, इसलिए कोष्ठक। मुझे नहीं लगता कि लोगों को घोषणाओं और कास्टिंग के बीच अंतर को समझने में कठिनाई होगी।
बेज़मैन

@abw x = (int) *ip;, dereferencing पॉइंटर द्वारा वैल्यू प्राप्त करें ipऔर intजो भी प्रकार ipहै उससे वैल्यू कास्ट करें ।
सुनील बोजानपल्ली

1
@ बेनोमीमन आप सही हैं: कास्टिंग और घोषणाएं दो अलग-अलग चीजें हैं। मैंने तारांकन चिह्न की अलग-अलग भूमिका में संकेत करने का प्रयास किया: 1 "यह एक इंट नहीं है, लेकिन" 2 "इंट करने के लिए एक सूचक है यह आपको इंट देगा, लेकिन इंट को सूचक नहीं"।
अरमान

2
@abw: यही वजह है शिक्षण int* bar = &foo;बनाता भार अधिक भावना। हां, मुझे पता है कि जब आप एक ही घोषणा में कई बिंदुओं की घोषणा करते हैं तो यह समस्या पैदा करता है। नहीं, मुझे नहीं लगता कि यह सब मायने रखता है।
कक्षा

17

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

  • किसी भी कोड को दिखाने से पहले, पॉइंटर्स कैसे काम करते हैं, यह स्पष्ट करने के लिए आरेख, रेखाचित्र या एनिमेशन का उपयोग करें।
  • सिंटैक्स प्रस्तुत करते समय, तारांकन चिह्न के दो अलग-अलग भूमिकाओं को स्पष्ट करें । कई ट्यूटोरियल गायब हैं या उस हिस्से को विकसित कर रहे हैं। भ्रम ensues ( "जब आप एक घोषणा और बाद में काम में एक initialized सूचक घोषणा को तोड़ने, आप * दूर करने के लिए याद करने के लिए है" - comp.lang.c पूछे जाने वाले प्रश्न ) मैं एक वैकल्पिक दृष्टिकोण को खोजने के लिए आशा व्यक्त की है, लेकिन मुझे लगता है कि यह है जाने का रास्ता।

आप अंतर को उजागर करने के int* barबजाय लिख सकते हैं int *bar। इसका मतलब यह है कि आप K & R "घोषणापत्र नकल का उपयोग नहीं करेंगे" दृष्टिकोण का पालन करें, लेकिन स्ट्रॉस्ट्रुप C ++ दृष्टिकोण :

हम *barपूर्णांक होने की घोषणा नहीं करते हैं । हम barएक होने की घोषणा करते हैं int*। यदि हम एक ही पंक्ति में एक नए बनाए गए चर को इनिशियलाइज़ करना चाहते हैं, तो यह स्पष्ट है कि हम साथ काम कर रहे हैं bar, नहीं *barint* bar = &foo;

कमियां:

  • आपको अपने छात्र को कई पॉइंटर डिक्लेरेशन मुद्दे ( int* foo, barबनाम) के बारे में चेतावनी देनी होगीint *foo, *bar ) के ।
  • आपको उन्हें चोट की दुनिया के लिए तैयार करना होगा । कई प्रोग्रामर चर के नाम से सटे तारांकन को देखना चाहते हैं, और वे अपनी शैली को सही ठहराने के लिए बड़ी लंबाई लेंगे। और कई स्टाइल गाइड इस नोटेशन को स्पष्ट रूप से लागू करते हैं (लिनक्स कर्नेल कोडिंग स्टाइल, नासा सी स्टाइल गाइड, आदि)।

संपादित करें: एक अलग दृष्टिकोण जो सुझाया गया है, वह है कश्मीर एंड आर "मिमिक" तरीके से जाना, लेकिन "शॉर्टहैंड" सिंटैक्स के बिना ( यहां देखें )। जैसे ही आप एक ही पंक्ति में एक घोषणा और एक असाइनमेंट करने से चूक जाते हैं , सब कुछ बहुत अधिक सुसंगत दिखाई देगा।

हालांकि, जल्दी या बाद में छात्र को फ़ंक्शन तर्क के रूप में संकेत से निपटना होगा। और रिटर्न प्रकार के रूप में इंगित करता है। और कार्यों को इंगित करता है। आपको int *func();और के बीच का अंतर स्पष्ट करना होगा int (*func)();। मुझे लगता है कि जल्द या बाद में चीजें अलग हो जाएंगी। और शायद जल्द ही बाद में से बेहतर है।


16

वहाँ एक कारण है कि कश्मीर और आर शैली एहसान int *pऔर स्ट्रॉस्ट्रुप शैली एहसान int* p; दोनों प्रत्येक भाषा में मान्य हैं (और एक ही चीज़ का मतलब है), लेकिन जैसा कि स्ट्रॉस्ट्रुप ने इसे रखा:

"Int * p;" के बीच चुनाव और "int * p?" सही और गलत के बारे में नहीं है, लेकिन शैली और जोर के बारे में है। सी ने ज़ोर दिया; घोषणाओं को अक्सर एक आवश्यक बुराई से थोड़ा अधिक माना जाता था। सी ++, दूसरी ओर, प्रकारों पर भारी जोर दिया गया है।

अब, जब से आप यहाँ C को पढ़ाने की कोशिश कर रहे हैं, तो आपको यह सुझाव देना चाहिए कि आपको उस प्रकार के भावों पर अधिक जोर देना चाहिए, लेकिन कुछ लोग आसानी से एक के बाद दूसरे की तुलना में तेज़ी से जोर लगा सकते हैं, और यह भाषा के बजाय उनके बारे में है।

इसलिए कुछ लोगों को इस विचार के साथ शुरुआत करना आसान होगा कि int*यह एक से अलग चीज है intऔर वहां से जाना है।

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

या नहीं। अगर कोई एक तरीका है कि हर कोई पहली बार अवधारणा को समझे तो आपको पहली बार में कोई समस्या नहीं होगी, और एक व्यक्ति को यह समझाने का सबसे अच्छा तरीका जरूरी नहीं होगा कि वह दूसरे को समझाए।


1
मुझे स्ट्रॉस्ट्रप का तर्क पसंद है, लेकिन मुझे आश्चर्य है कि उन्होंने संदर्भों को निरूपित करने के लिए & प्रतीक को क्यों चुना - एक और संभावित नुकसान।
आर्मिन

1
@abw मुझे लगता है कि उन्होंने समरूपता देखी अगर हम कर सकते हैं int* p = &aतो हम कर सकते हैं int* r = *p। मुझे पूरा यकीन है कि उसने इसे C ++ के द डिज़ाइन एंड इवोल्यूशन में कवर किया था , लेकिन जब से मैंने इसे पढ़ा है, तब तक यह एक लंबा समय है और मैं मूर्खतापूर्वक अपनी कॉपी किसी को दे रहा हूं।
जॉन हन्ना

3
मुझे लगता है कि आप का मतलब है int& r = *p। और मुझे यकीन है कि उधारकर्ता अभी भी पुस्तक को पचाने की कोशिश कर रहा है।
अरमान

@abw, हाँ, मेरा वही मतलब था। टिप्पणियों में एएलएस टाइपोस संकलन त्रुटियों को नहीं बढ़ाते हैं। पुस्तक वास्तव में काफी तेज है।
जॉन हन्ना

4
C के ऊपर पास्कल के सिंटैक्स (लोकप्रिय रूप से विस्तारित) के कारणों में से एक यह Var A, B: ^Integer;स्पष्ट करता है कि टाइप "पॉइंटर टू पूर्णांक" दोनों पर लागू होता है Aऔर B। एक K&Rशैली int *a, *bका उपयोग करना भी व्यावहारिक है; लेकिन एक घोषणा की तरह int* a,b;, तथापि, के रूप में यद्यपि दिखता है aऔर bदोनों घोषित किया जा रहा है के रूप में int*है, लेकिन वास्तविकता में यह घोषणा करता है aएक के रूप में int*और bएक के रूप में int
सुपरकाट

9

tl; डॉ:

प्रश्न: एक शुरुआत करने के लिए सी पॉइंटर्स (घोषणा बनाम एकरी संचालकों) की व्याख्या कैसे करें?

न करें। शुरुआती को संकेत समझाएं, और उन्हें दिखाएं कि सी सिंटैक्स में उनके सूचक अवधारणाओं का प्रतिनिधित्व कैसे करें।


मुझे हाल ही में एक सी प्रोग्रामिंग शुरुआत की ओर इशारा करने वाले लोगों को समझाने में खुशी हुई है और निम्नलिखित कठिनाई पर ठोकर खाई है।

IMO सी वाक्यविन्यास भयानक नहीं है, लेकिन यह या तो अद्भुत नहीं है: यह न तो एक बड़ी बाधा है यदि आप पहले से ही संकेत समझते हैं, और न ही उन्हें सीखने में कोई मदद करते हैं।

इसलिए: संकेत समझाकर शुरू करें, और सुनिश्चित करें कि वे वास्तव में उन्हें समझते हैं:

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

  • स्यूडोकोड के साथ समझाएं: बार में संग्रहीत फू और मूल्य का पता लिखें ।

  • फिर, जब आपका नौसिखिया समझता है कि संकेत क्या हैं, और क्यों, और उनका उपयोग कैसे करें; फिर सी सिंटैक्स पर मानचित्रण दिखाएं।

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


वास्तव में; पहले सिद्धांत से शुरू करें, वाक्यविन्यास बाद में आता है (और महत्वपूर्ण नहीं है)। ध्यान दें कि स्मृति उपयोग का सिद्धांत भाषा पर निर्भर नहीं है। यह बॉक्स-एंड-एरो मॉडल आपको किसी भी प्रोग्रामिंग भाषा में कार्यों में मदद करेगा।
o

कुछ उदाहरणों के लिए यहां देखें (हालांकि Google भी मदद करेगा) eskimo.com/~scs/cclass/notes/sx10a.html
oa

7

सी सीखने के लिए शुरू करते समय यह समस्या कुछ हद तक भ्रामक है।

यहां मूल सिद्धांत दिए गए हैं जो आपको आरंभ करने में मदद कर सकते हैं:

  1. C में कुछ मूल प्रकार हैं:

    • char: 1 बाइट के आकार के साथ एक पूर्णांक मान।

    • short: 2 बाइट्स के आकार के साथ एक पूर्णांक मान।

    • long: 4 बाइट्स के आकार के साथ एक पूर्णांक मान।

    • long long: 8 बाइट्स के आकार के साथ एक पूर्णांक मान।

    • float: 4 बाइट्स के आकार के साथ एक गैर-पूर्णांक मान।

    • double: 8 बाइट्स के आकार के साथ एक गैर-पूर्णांक मान।

    ध्यान दें कि प्रत्येक प्रकार का आकार आम तौर पर संकलक द्वारा निर्धारित किया जाता है और मानक द्वारा नहीं।

    पूर्णांक प्रकार short, longऔर long longआमतौर पर इसके बाद होते हैंint

    हालांकि, यह एक जरूरी नहीं है, और आप उन्हें बिना उपयोग कर सकते हैं int

    वैकल्पिक रूप से, आप केवल राज्य कर सकते हैं int , लेकिन अलग-अलग संकलक द्वारा अलग-अलग व्याख्या की जा सकती है।

    इसलिए इसे संक्षेप में प्रस्तुत करने के लिए:

    • shortरूप में ही है short intलेकिन जरूरी नहीं कि के रूप में ही int

    • longरूप में ही है long intलेकिन जरूरी नहीं कि के रूप में ही int

    • long longरूप में ही है long long intलेकिन जरूरी नहीं कि के रूप में ही int

    • दिए गए संकलक पर, या intतो है या ।short intlong intlong long int

  2. यदि आप किसी प्रकार का एक वैरिएबल घोषित करते हैं, तो आप एक अन्य वैरिएबल को भी इंगित कर सकते हैं।

    उदाहरण के लिए:

    int a;

    int* b = &a;

    तो संक्षेप में, प्रत्येक मूल प्रकार के लिए, हमारे पास एक संगत सूचक प्रकार भी है।

    उदाहरण के लिए: shortऔर short*

    "वैरिएबल" को देखने के दो तरीके हैं b (यह शायद सबसे शुरुआती को भ्रमित करता है) :

    • आप bएक प्रकार के चर के रूप में विचार कर सकते हैं int*

    • आप *bएक प्रकार के चर के रूप में विचार कर सकते हैं int

    इसलिए, कुछ लोग घोषणा करेंगे int* b, जबकि अन्य घोषित करेंगे int *b

    लेकिन इस तथ्य का तथ्य यह है कि ये दोनों घोषणाएं समान हैं (रिक्त स्थान निरर्थक हैं)।

    आप या तो bएक पूर्णांक मान के लिए सूचक के *bरूप में , या वास्तविक इंगित पूर्णांक मान के रूप में उपयोग कर सकते हैं ।

    आप प्राप्त मूल्य को पढ़ सकते हैं (:) int c = *b

    और आप बताए गए मान को सेट (लिख) सकते हैं *b = 5:।

  3. एक पॉइंटर किसी भी मेमोरी एड्रेस को इंगित कर सकता है, और न केवल आपके द्वारा पहले घोषित किए गए कुछ वेरिएबल के पते पर। हालाँकि, आपको इंगित स्मृति पते पर स्थित मान प्राप्त करने या सेट करने के लिए पॉइंटर्स का उपयोग करते समय सावधान रहना चाहिए।

    उदाहरण के लिए:

    int* a = (int*)0x8000000;

    यहां, हमारे पास aमेमोरी एड्रेस 0x8000000 पर वेरिएबल पॉइंटिंग है।

    यदि यह मेमोरी पता आपके प्रोग्राम की मेमोरी स्पेस के भीतर मैप नहीं किया गया है, तो *aमेमोरी एक्सेस उल्लंघन के कारण किसी भी पढ़ने या लिखने के ऑपरेशन से आपके प्रोग्राम के क्रैश होने की संभावना सबसे अधिक होगी।

    आप सुरक्षित रूप से के मूल्य को बदल सकते हैं a, लेकिन आपको बहुत सावधान रहना चाहिए ताकि मूल्य बदल जाए *a

  4. प्रकार void*इस तथ्य में असाधारण है कि इसमें एक "मूल्य प्रकार" नहीं है जिसका उपयोग किया जा सकता है (यानी, आप घोषित नहीं कर सकते हैं void a)। इस प्रकार का उपयोग केवल एक सामान्य सूचक के रूप में एक मेमोरी पते के लिए किया जाता है, उस पते में रहने वाले डेटा के प्रकार को निर्दिष्ट किए बिना।


7

शायद इसके माध्यम से कदम बस थोड़ा और आसान हो जाता है:

#include <stdio.h>

int main()
{
    int foo = 1;
    int *bar = &foo;
    printf("%i\n", foo);
    printf("%p\n", &foo);
    printf("%p\n", (void *)&foo);
    printf("%p\n", &bar);
    printf("%p\n", bar);
    printf("%i\n", *bar);
    return 0;
}

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

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

अगला कदम उन्हें आपको समझाने के लिए प्राप्त करना है किस तरह i[0]से संबंधित हैं &i। यदि वे ऐसा कर सकते हैं, तो वे इसे नहीं भूलेंगे और आप समय से थोड़ा आगे भी, ढांचों के बारे में बात करना शुरू कर सकते हैं।

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


यह एक अच्छा व्यायाम है। लेकिन जिस मुद्दे को मैं सामने लाना चाहता था, वह एक विशिष्ट वाक्य-विन्यास है जो छात्रों के मानसिक मॉडल पर प्रभाव डाल सकता है। इस पर विचार करें int foo = 1;:। अब यह ठीक है int *bar; *bar = foo;:। यह ठीक नहीं है:int *bar = foo;
armin

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

6

अभिव्यक्ति *bar का प्रकार है int; इस प्रकार, चर (और अभिव्यक्ति) का प्रकार barहै int *। चूँकि चर में सूचक प्रकार होता है, इसलिए इसके आरंभक में सूचक प्रकार भी होना चाहिए।

सूचक चर आरंभीकरण और असाइनमेंट के बीच एक असंगतता है; यह सिर्फ कुछ ऐसा है जिसे कठिन तरीके से सीखना है।


3
यहाँ के जवाबों को देखकर मुझे लग रहा है कि कई अनुभवी प्रोग्रामर भी अब इस समस्या को नहीं देख सकते। मुझे लगता है कि "विसंगतियों के साथ जीना सीखना" का एक प्रतिफल है।
armin

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

5

मैं इसे पहले *से intअधिक के लिए लागू के रूप में पढ़ा था bar

int  foo = 1;           // foo is an integer (int) with the value 1
int* bar = &foo;        // bar is a pointer on an integer (int*). it points on foo. 
                        // bar value is foo address
                        // *bar value is foo value = 1

printf("%p\n", &foo);   // print the address of foo
printf("%p\n", bar);    // print the address of foo
printf("%i\n", foo);    // print foo value
printf("%i\n", *bar);   // print foo value

2
फिर आपको यह समझाना int* a, bहोगा कि वे ऐसा क्यों नहीं करते जो उन्हें लगता है कि यह करता है।
चरण

4
यह सच है, लेकिन मुझे नहीं लगता कि int* a,bइसका इस्तेमाल किया जाना चाहिए। बेहतर लिज़िबिलिटी, अपडेटिंग आदि के लिए ... प्रति लाइन केवल एक चर घोषणा होनी चाहिए और कभी भी अधिक नहीं। यह शुरुआती लोगों को भी समझाने के लिए कुछ है, भले ही संकलक इसे संभाल सकता है।
ग्रेलेल

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

1
मैं @grorel से सहमत हूं। *इस प्रकार के भाग के रूप में सोचना आसान है , और बस हतोत्साहित करना int* a, b। जब तक आप यह कहना पसंद नहीं करते कि वह *aएक प्रकार की intबजाय aएक पॉइंटर है int...
केविन उशी

@grorel सही है: int *a, b;इसका उपयोग नहीं किया जाना चाहिए। एक ही कथन में विभिन्न प्रकारों के साथ दो चर की घोषणा करना बहुत खराब अभ्यास है और लाइन के नीचे रखरखाव के मुद्दों के लिए एक मजबूत उम्मीदवार है। शायद यह हम में से जो लोग एम्बेडेड क्षेत्र है, जहां एक में काम के लिए अलग है int*और एक intअक्सर विभिन्न आकारों हैं और कभी कभी पूरी तरह से अलग स्मृति स्थल में संग्रहीत। यह C भाषा के कई पहलुओं में से एक है जिसे 'इसकी अनुमति है, लेकिन यह मत करो' के रूप में सबसे अच्छा पढ़ाया जाएगा।
ईविल डॉग पाई

5
int *bar = &foo;

Question 1: क्या है bar?

Ans: यह एक सूचक चर (टाइप करने के लिए int) है। एक पॉइंटर को कुछ मान्य मेमोरी लोकेशन की ओर इशारा करना चाहिए और बाद *में उस स्थान पर संग्रहीत मान को पढ़ने के लिए एक अपर संचालक का उपयोग करते हुए डीरिफ़र्ड (* बार) होना चाहिए ।

Question 2: क्या है &foo?

Ans: foo टाइप का एक वैरिएबल है int। जो कुछ वैध मेमोरी लोकेशन में स्टोर होता है और वह लोकेशन हमें ऑपरेटर से मिलता है &इसलिए अब जो हमारे पास है वह कुछ मान्य मेमोरी लोकेशन है &foo

इसलिए दोनों को एक साथ रखा गया था। पॉइंटर को एक आवश्यक मेमोरी लोकेशन चाहिए था और यह &fooआरंभिक रूप से अच्छा है।

अब पॉइंटर barवैध मेमोरी लोकेशन की ओर इशारा कर रहा है और इसमें स्टोर किए गए मूल्य को इसे डीफ्रेंसिंग के रूप में प्राप्त किया जा सकता है*bar


5

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


4

मुझे लगता है कि अंतरिक्ष में शैतान है।

मैं लिखता हूं (न केवल शुरुआत के लिए, बल्कि खुद के लिए भी): int * bar = & foo; int के बजाय * bar = & foo;

यह स्पष्ट करना चाहिए कि वाक्य रचना और शब्दार्थ के बीच क्या संबंध है


4

यह पहले से ही नोट किया गया था कि * कई भूमिकाएँ हैं।

एक और सरल विचार है जो चीजों को समझने के लिए एक शुरुआत में मदद कर सकता है:

सोचें कि "=" की भी कई भूमिकाएँ हैं।

जब असाइनमेंट को घोषणा के साथ एक ही लाइन पर उपयोग किया जाता है, तो इसे एक निर्माता के रूप में सोचें, न कि एक मनमाना असाइनमेंट।

जब आप देखते है:

int *bar = &foo;

सोचें कि यह लगभग बराबर है:

int *bar(&foo);

कोष्ठक तारांकन पर अधिकता लेते हैं, इसलिए "* बार" के बजाय "बार" को सहज रूप से "बार" के लिए जिम्मेदार ठहराया जाता है।


4

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

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

int x;

एक्स को एक इंट घोषित करता है: एक्सप्रेशन 'x' में इंट इंट होगा। सामान्य तौर पर, यह पता लगाने के लिए कि नए चर के प्रकार को कैसे लिखना है, उस चर को शामिल करने वाली एक अभिव्यक्ति लिखें जो मूल प्रकार का मूल्यांकन करता है, फिर मूल प्रकार को बाईं ओर और दाईं ओर अभिव्यक्ति लिखें।

इस प्रकार, घोषणाएँ

int *p;
int a[3];

यह बताएं कि p, int का सूचक है क्योंकि '* p' का प्रकार int है, और यह कि एक ints का एक सरणी है क्योंकि एक [3] (विशेष सूचकांक मान को अनदेखा करता है, जिसे सरणी का आकार माना जाता है) प्रकार है पूर्णांक।

(यह इस बात को बताता है कि इस समझ को कार्य बिंदुओं आदि तक कैसे बढ़ाया जाए)

यह एक ऐसा तरीका है, जिसके बारे में मैंने पहले सोचा नहीं था, लेकिन यह वाक्य रचना के अतिभार के लिए लेखांकन का एक बहुत ही सरल तरीका लगता है।


3

यदि समस्या सिंटैक्स है, तो टेम्पलेट / उपयोग के साथ समकक्ष कोड दिखाने में मदद मिल सकती है।

template<typename T>
using ptr = T*;

यह तब के रूप में इस्तेमाल किया जा सकता है

ptr<int> bar = &foo;

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


2
शुरुआती लोगों के लिए यह बहुत अधिक भ्रमित करने वाला होगा।
कार्स्टन

मेरा हालांकि यह था कि आप ptr की परिभाषा नहीं दिखाएंगे। बस इसे सूचक घोषणाओं के लिए उपयोग करें।
MI3Guy

3

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

घोषणा में

int *bar = &foo;  

*प्रतीक है अविवेक ऑपरेटर नहीं । इसके बजाय, यह barसंकलक को सूचित करने के प्रकार को निर्दिष्ट करने में मदद करता है जो कि barएक सूचक हैint । दूसरी ओर, जब यह एक स्टेटमेंट में दिखाई देता है तो *प्रतीक (जब एक अपर संचालक के रूप में उपयोग किया जाता है ) अप्रत्यक्ष प्रदर्शन करता है। इसलिए, बयान

*bar = &foo;

यह गलत होगा क्योंकि यह fooउस वस्तु का पता barबताता है जो barस्वयं को नहीं, को इंगित करता है।


3

"शायद इसे इंट * बार के रूप में लिखना यह अधिक स्पष्ट करता है कि स्टार वास्तव में प्रकार का हिस्सा है, पहचानकर्ता का हिस्सा नहीं है।" इसलिए मैं करता हूँ। और मैं कहता हूं, कि यह टाइपिंग की तरह है, लेकिन केवल एक पॉइंटर नाम के लिए।

"बेशक यह आपको अलग-अलग समस्याओं में चलाता है जैसे कि int *, b।


2

यहां आपको संकलक तर्क का उपयोग, समझना और व्याख्या करना है, न कि मानवीय तर्क (मुझे पता है, आपको) एक मानव हैं, लेकिन यहां आपको कंप्यूटर की नकल करनी होगी ...)।

जब आप लिखते हैं

int *bar = &foo;

संकलक समूहों कि के रूप में

{ int * } bar = &foo;

वह है: यहाँ एक नया चर है, इसका नाम है bar, इसका प्रकार int के लिए सूचक है, और इसका प्रारंभिक मूल्य है&foo

और आपको जोड़ना चाहिए: =ऊपर एक आरंभीकरण को दर्शाता है एक प्रभाव नहीं, जबकि निम्नलिखित अभिव्यक्तियों में *bar = 2;यह है एक दिखावा

प्रति टिप्पणी संपादित करें:

सावधान: कई घोषणा के मामले में *केवल निम्नलिखित चर से संबंधित है:

int *bar = &foo, b = 2;

बार, फू के पते से आरंभ करने के लिए int का सूचक है, b, 2 के लिए एक प्रारंभिक है, और अंदर है

int *bar=&foo, **p = &bar;

बार अभी भी सूचक को int में, और p पता या बार के आरंभ में int के लिए सूचक के लिए एक सूचक है।


2
वास्तव में संकलक इसे इस तरह से समूहित नहीं करता है: int* a, b;एक को एक के लिए एक सूचक होने की घोषणा करता है int, लेकिन एक होने के लिए बी int*प्रतीक सिर्फ दो अलग-अलग अर्थ है: एक घोषणा में, यह एक सूचक प्रकार इंगित करता है, और एक अभिव्यक्ति में यह एकल भिन्नता ऑपरेटर है।
tmlen

@tmlen: मेरा मतलब यह है कि आरंभीकरण में, *टाइप करने के लिए rattached में, ताकि पॉइंटर को इनिशियलाइज़ किया जाए जबकि एक प्रभाव में इंगित मूल्य प्रभावित होता है। लेकिन कम से कम आपने मुझे एक अच्छी टोपी दी :-)
सर्ज बलेस्टा

0

मूल रूप से पॉइंटर एक सरणी संकेत नहीं है। शुरुआत आसानी से सोचती है कि सूचक सरणी की तरह दिखता है। का उपयोग करते हुए अधिकांश स्ट्रिंग उदाहरण

"चार * pstr" यह समान दिखता है

"चार स्ट्रोक [80]"

लेकिन, महत्वपूर्ण बातें, पॉइंटर को कंपाइलर के निचले स्तर में सिर्फ पूर्णांक के रूप में माना जाता है।

आइए उदाहरण देखें ::

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv, char **env)
{
    char str[] = "This is Pointer examples!"; // if we assume str[] is located in 0x80001000 address

    char *pstr0 = str;   // or this will be using with
    // or
    char *pstr1 = &str[0];

    unsigned int straddr = (unsigned int)pstr0;

    printf("Pointer examples: pstr0 = %08x\n", pstr0);
    printf("Pointer examples: &str[0] = %08x\n", &str[0]);
    printf("Pointer examples: str = %08x\n", str);
    printf("Pointer examples: straddr = %08x\n", straddr);
    printf("Pointer examples: str[0] = %c\n", str[0]);

    return 0;
}

परिणाम इस तरह होंगे 0x2a6b7ed0 str का पता है []

~/work/test_c_code$ ./testptr
Pointer examples: pstr0 = 2a6b7ed0
Pointer examples: &str[0] = 2a6b7ed0
Pointer examples: str = 2a6b7ed0
Pointer examples: straddr = 2a6b7ed0
Pointer examples: str[0] = T

तो, मूल रूप से, ध्यान रखें कि सूचक किसी प्रकार का पूर्णांक है। पता प्रस्तुत करना।


-1

मैं समझाता हूं कि किन्नर ऑब्जेक्ट हैं, जैसे कि फ्लोट्स आदि। एक पॉइंटर एक प्रकार का ऑब्जेक्ट होता है, जिसका मूल्य मेमोरी में एक एड्रेस को दर्शाता है (इसलिए क्यों एक पॉइंटर NULL को डिफॉल्ट करता है)।

जब आप पहली बार एक पॉइंटर घोषित करते हैं तो आप टाइप-पॉइंटर-नाम सिंटैक्स का उपयोग करते हैं। इसे "पूर्णांक-सूचक नाम" के रूप में पढ़ा जाता है जो किसी भी पूर्णांक वस्तु के पते को इंगित कर सकता है। हम केवल घोषणा के दौरान इस वाक्यविन्यास का उपयोग करते हैं, इसी तरह से हम एक int को 'int num1' घोषित करते हैं, लेकिन हम केवल 'num1' का उपयोग करते हैं जब हम उस चर का उपयोग करना चाहते हैं, 'int num1' का नहीं।

int x = 5; // 5 के मान के साथ एक पूर्णांक वस्तु

int * ptr; // डिफ़ॉल्ट रूप से NULL के मान के साथ एक पूर्णांक

किसी ऑब्जेक्ट के पते पर एक पॉइंटर पॉइंट बनाने के लिए हम '&' सिंबल का उपयोग करते हैं जिसे "एड्रेस ऑफ" के रूप में पढ़ा जा सकता है।

पीटीआर = & x; // अब मान 'x' का पता है

जैसा कि पॉइंटर केवल ऑब्जेक्ट का पता होता है, उस पते पर आयोजित वास्तविक मूल्य प्राप्त करने के लिए हमें '*' चिन्ह का उपयोग करना चाहिए जो कि पॉइंटर से पहले उपयोग होने पर "द्वारा इंगित किए गए पते पर मूल्य" का उपयोग करता है।

std :: cout << * ptr; // पता पर मूल्य का प्रिंट आउट लें

आप संक्षेप में समझा सकते हैं कि ' ' एक 'ऑपरेटर' है जो विभिन्न प्रकार की वस्तुओं के साथ अलग-अलग परिणाम देता है। जब एक पॉइंटर के साथ प्रयोग किया जाता है, ' ' ऑपरेटर का मतलब "गुणा नहीं" होता है।

यह एक आरेख को यह दिखाने में मदद करता है कि चर का नाम और मान कैसे है और सूचक का पता (नाम) और मान है और यह दर्शाता है कि सूचक का मान int का पता होगा।


-1

एक सूचक केवल पतों को संग्रहीत करने के लिए उपयोग किया जाने वाला एक चर है।

कंप्यूटर में मेमोरी बाइट्स से बनी होती है (एक बाइट में 8 बिट होते हैं) क्रमबद्ध तरीके से व्यवस्थित होते हैं। प्रत्येक बाइट में एक संख्या होती है, जैसे कि किसी सरणी में इंडेक्स या सबस्क्रिप्ट के समान, जिसे बाइट का पता कहा जाता है। बाइट का पता 0 से शुरू होकर मेमोरी के आकार से कम होता है। उदाहरण के लिए, 64MB RAM में कहें, 64 * 2 ^ 20 = 67108864 बाइट्स हैं। इसलिए इन बाइट्स का पता 0 से 67108863 तक शुरू होगा।

यहां छवि विवरण दर्ज करें

आइए देखें कि जब आप एक चर घोषित करते हैं तो क्या होता है।

इंट मार्क्स;

जैसा कि हम जानते हैं कि एक int अधिभोग 4 बाइट्स डेटा (यह मानते हुए कि हम एक 32-बिट संकलक का उपयोग कर रहे हैं), इसलिए संकलक एक पूर्णांक मान को संग्रहीत करने के लिए मेमोरी से 4 लगातार बाइट्स रखता है। 4 आवंटित बाइट्स के पहले बाइट का पता चर के निशान के रूप में जाना जाता है। मान लीजिए कि 4 लगातार बाइट्स का पता 5004, 5005, 5006 और 5007 है तो वेरिएबल मार्क्स का पता 5004 होगा। यहां छवि विवरण दर्ज करें

सूचक चर की घोषणा

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

वाक्य - विन्यास: data_type *pointer_name;

data_type पॉइंटर का प्रकार है (पॉइंटर के आधार प्रकार के रूप में भी जाना जाता है)। पॉइंटर_नाम चर का नाम है, जो किसी भी मान्य सी पहचानकर्ता हो सकता है।

आइए कुछ उदाहरण लेते हैं:

int *ip;

float *fp;

int * ip का मतलब है कि ip एक पॉइंटर वैरिएबल है जो टाइप इंट के वेरिएबल्स की ओर इशारा करने में सक्षम है। दूसरे शब्दों में, एक पॉइंटर वैरिएबल आईपी केवल प्रकार के चर के पते को स्टोर कर सकता है। इसी तरह, पॉइंटर वेरिएबल fp केवल टाइप फ्लोट के वेरिएबल के एड्रेस को स्टोर कर सकता है। चर का प्रकार (बेस प्रकार के रूप में भी जाना जाता है) आईपी इंट का सूचक है और एफपी का प्रकार फ्लोट के लिए एक संकेतक है। Int को टाइप पॉइंटर के एक पॉइंटर वैरिएबल को प्रतीकात्मक रूप से (int *) के रूप में दर्शाया जा सकता है। इसी प्रकार, फ्लोट * के प्रकार पॉइंटर के एक पॉइंटर वैरिएबल को फ्लोट * के रूप में दर्शाया जा सकता है।

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

int *ip, i = 10;
float *fp, f = 12.2;

ip = &i;
fp = &f;

स्रोत: thecguru अब तक का सबसे सरल लेकिन विस्तृत विवरण है जो मैंने अभी तक पाया है।

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