सी पॉइंटर्स के बारे में लोगों को क्या मुश्किल लगता है? [बन्द है]


173

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

मैं यह जानने के लिए उत्सुक हूं कि क्यों। उन्होंने वास्तव में मुझे कभी भी बड़ी समस्या नहीं दी है (हालाँकि मैंने पहली बार उनके बारे में नवपाषाण काल ​​में सीखा था)। इन सवालों के बेहतर उत्तर लिखने के लिए, मैं जानना चाहता हूं कि लोगों को क्या मुश्किल लगता है।

इसलिए, यदि आप पॉइंटर्स के साथ संघर्ष कर रहे हैं, या आप हाल ही में थे, लेकिन अचानक "मिल गया", तो पॉइंटर्स के कौन से पहलू थे जिनकी वजह से आपको परेशानी हुई?


54
Snarky, अतिरंजित, अनावश्यक रूप से विवादास्पद, सच्चाई के एक दाने के साथ जवाब: वे अपंग हो गए हैं --- मानसिक रूप से सताए हुए, मैं 'हां --- बताती हूं कि उनमें से एक "अभिव्यंजक" उच्च स्तर की भाषाएं हैं। उन्हें नंगे धातु पर प्रोग्रामिंग करके शुरू करना चाहिए जैसे कि भगवान एक डैनियल बूने!
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

3
... और प्रोग्रामर बेहतर होगा क्योंकि यह आपके सर्वोत्तम प्रयासों के बावजूद चर्चा में विकसित होगा
dmckee --- पूर्व-संचालक बिल्ली का बच्चा

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

2
@ सॅम सैफ्रन: जबकि मैं आमतौर पर सहमत हूँ कि यह एक प्रोग्रामर का अधिक है। एक प्रकार का प्रश्न है, ईमानदारी से यह बुरा नहीं होगा यदि लोग "मुझे लगता है कि यह आसान है" और "मुझे देखने के संकेत से नफरत है" स्पैम के रूप में चिह्नित करने के लिए तैयार थे। वो हैं।
जेकरियन

3
किसी को इसे ऊपर लाना है: "यह चंद्रमा की ओर इशारा करते हुए उंगली की तरह है। उंगली पर ध्यान केंद्रित न करें या आप उस सभी स्वर्गीय मिस को याद करेंगे" - ब्रूस ली
म्यू

जवाबों:


86

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

जब मैं पढ़ा रहा था, तब मुझे विद्यार्थियों की समझ में निम्नलिखित छेद मिले जो समस्याओं का सबसे आम स्रोत थे:

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

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

मैं संक्षेप में अनुमान लगाता हूं, मैं कह रहा हूं कि यदि आप संकेत को समझना चाहते हैं, तो आपको चर को समझना होगा, और वे वास्तव में आधुनिक आर्किटेक्चर में क्या हैं।


13
IMHO, स्टैक और हीप को समझना निम्न स्तर के सीपीयू विवरण के रूप में अनावश्यक है। स्टैक और ढेर कार्यान्वयन विवरण हो। ISO C कल्पना में "स्टैक" शब्द का एक भी उल्लेख नहीं है और न ही K & R है।
15:27 बजे सिगाजाइस

4
@ सिगाजाइस: आपकी आपत्तियां सवाल और जवाब दोनों की बात को याद करती हैं। A) K & R C एक अभिग्राहकता B है) ISO C केवल पॉइंटर्स वाली भाषा नहीं है, मेरे अंक 1 और 2 एक गैर-सी-आधारित भाषा C के विरुद्ध विकसित किए गए थे) 95% आर्किटेक्चर (नहीं भाषाएं) बाहर ढेर का उपयोग करते हैं / स्टैक सिस्टम, यह काफी सामान्य है कि अपवादों को इसके सापेक्ष समझाया गया है। डी) प्रश्न का बिंदु था "लोग पॉइंटर्स को क्यों नहीं समझते हैं", "मैं आईएसओ सी कैसे
समझाऊं

9
@ जॉन मार्खेट्टी: इससे भी अधिक ... यह देखते हुए कि यह सवाल था कि "लोगों के साथ समस्या का मूल मुद्दा क्या है", मुझे नहीं लगता कि लोग सूचक से संबंधित प्रश्न पूछ रहे हैं, "आप डॉन 'से बहुत प्रभावित होंगे टी वास्तव में एक उत्तर के रूप में "जानने की जरूरत है। जाहिर है वे असहमत हैं। :)
जेकरियन

3
@jkerian यह पुराना हो सकता है, लेकिन K & R के तीन या चार पृष्ठ जो बताते हैं कि कार्यान्वयन विवरण की आवश्यकता के बिना संकेत करते हैं। कार्यान्वयन विवरण का ज्ञान विभिन्न कारणों से उपयोगी है, लेकिन IMHO, यह किसी भाषा के प्रमुख निर्माणों को समझने के लिए आवश्यक नहीं होना चाहिए।
सिगाजिस

3
"आम तौर पर विभिन्न स्थानों पर स्पष्ट काल्पनिक पते देने में मदद मिली।" -> +1
fredoverflow

146

जब मैंने पहली बार उनके साथ काम करना शुरू किया, तो मेरे पास सबसे बड़ी समस्या सिंटैक्स की थी।

int* ip;
int * ip;
int *ip;

सभी समान हैं।

परंतु:

int* ip1, ip2;  //second one isn't a pointer!
int *ip1, *ip2;

क्यों? क्योंकि घोषणा का "सूचक" भाग चर का है, न कि प्रकार का।

और फिर बात को डीफ्रेंसिंग करते हुए एक बहुत ही समान संकेतन का उपयोग किया जाता है:

*ip = 4;  //sets the value of the thing pointed to by ip to '4'
x = ip;   //hey, that's not '4'!
x = *ip;  //ahh... there's that '4'

सिवाय जब आपको वास्तव में एक संकेतक प्राप्त करने की आवश्यकता होती है ... तो आप एक एम्परसेंड का उपयोग करते हैं!

int *ip = &x;

संगति के लिए हुर्रे!

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

void foo(****ipppArr);

इसे कॉल करने के लिए, मुझे पॉइंटर्स के पॉइंट टू पॉइंटर्स टू इन्टर्स के पॉइंट ऑफ़ व्यू की जरूरत है:

foo(&(***ipppArr));

छह महीने में, जब मुझे इस कोड को बनाए रखना है, तो मैं यह जानने में अधिक समय बिताऊंगा कि यह सब जमीन से पुनर्लेखन का क्या मतलब है। (हाँ, शायद उस वाक्यविन्यास को गलत मिला - सी में कुछ भी करने के बाद से यह समय हो गया है। मैं थोड़े इसे याद करता हूं, लेकिन फिर मैं एक जनवादी हूं।)


21
पहले एक पर आपकी टिप्पणी, >> * आईपी = 4; // '4' के लिए IP का मान सेट करता है << गलत है। यह होना चाहिए >> // आईपी द्वारा '4' को इंगित की गई चीज़ का मान सेट करता है
आआआह bbbb

8
किसी भी भाषा में एक दूसरे के ऊपर कई प्रकार के स्टैकिंग करना एक बुरा विचार है। आप लिख सकते हैं "फू (और (*** ipppArr));" C में अजीब बात है, लेकिन "std :: map <std :: pair <int, int>, std :: जोड़ा <std :: वेक्टर <int>, std :: tuple <int, double, std :: सूची C ++ में <int >>>> "भी बहुत जटिल है। इसका मतलब यह नहीं है कि सी ++ में सी या एसटीएल कंटेनरों में संकेत जटिल हैं। इसका सिर्फ यह मतलब है कि आपको अपने कोड के पाठक के लिए इसे समझने के लिए बेहतर प्रकार की परिभाषाओं का उपयोग करना होगा।
पैट्रिक

20
मैं ईमानदारी से विश्वास नहीं कर सकता कि वाक्य रचना की गलतफहमी सबसे भारी मतदान का जवाब है। यह पॉइंटर्स के बारे में सबसे आसान हिस्सा है।
जेसन

4
इस उत्तर को पढ़ते हुए भी, मुझे कागज की एक शीट पाने और तस्वीर खींचने के लिए लुभाया गया। सी में, मैं हमेशा चित्र बना रहा था।
माइकल ईस्टर

19
@ जेसन कठिनाई का उद्देश्य क्या है जो लोगों के बहुमत को खोजने में मुश्किल के अलावा अन्य है?
रूपर्ट मैडेन-एबॉट

52

पॉइंटर्स की उचित समझ के लिए अंतर्निहित मशीन की वास्तुकला के बारे में ज्ञान की आवश्यकता होती है।

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


18
@ मार्की: अच्छा, क्या मैं गलत हूँ? कितने जावा प्रोग्रामर सेगफॉल्ट से निपट सकते हैं?
रॉबर्ट हार्वे

5
क्या सेगफ्यूल्स स्टिक शिफ्ट के साथ कुछ करना है? - एक जावा प्रोग्रामर
टॉम एंडरसन

6
@ रॉबर्ट: यह एक वास्तविक पूरक के रूप में था। यह लोगों की भावनाओं को आहत किए बिना चर्चा करने के लिए एक कठिन विषय है। और मुझे डर है कि मेरी टिप्पणी से मुझे बहुत संघर्ष का सामना करना पड़ा जिसे मैंने सोचा कि आप बचने में कामयाब होंगे। मया कपला।
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

30
मैं असहमत हूं; आपको संकेत प्राप्त करने के लिए अंतर्निहित वास्तुकला को समझने की आवश्यकता नहीं है (वे एक अमूर्त हैं, वैसे भी)।
जेसन

11
@ जेसन: सी में, एक सूचक अनिवार्य रूप से एक मेमोरी एड्रेस है। मशीन वास्तुकला को समझे बिना उनके साथ सुरक्षित रूप से काम करना असंभव है। देखें en.wikipedia.org/wiki/Pointer_(computing) और boredzo.org/pointers/#definition
रॉबर्ट हार्वे

42

पॉइंटर्स के साथ काम करते समय, भ्रमित होने वाले लोग दो में से एक शिविर में व्यापक रूप से होते हैं। मैं दोनों में (हूँ?) हूँ।

array[]भीड़

यह वह भीड़ है जो सीधे नहीं बताती है कि सूचक संकेतन से सरणी संकेतन में अनुवाद कैसे किया जाता है (या यह भी नहीं जानता कि वे संबंधित भी हैं)। किसी ऐरे के तत्वों तक पहुंचने के चार तरीके यहां दिए गए हैं:

  1. सरणी नाम के साथ सरणी संकेतन (अनुक्रमणिका)
  2. सूचक नाम के साथ सरणी संकेतन (अनुक्रमण)
  3. सूचक संकेतन (*) सूचक नाम के साथ
  4. सूचक नाम (*) सरणी नाम के साथ

 

int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;

array       element            pointer
notation    number     vals    notation

vals[0]     0          10      *(ptr + 0)
ptr[0]                         *(vals + 0)

vals[1]     1          20      *(ptr + 1)
ptr[1]                         *(vals + 1)

vals[2]     2          30      *(ptr + 2)
ptr[2]                         *(vals + 2)

vals[3]     3          40      *(ptr + 3)
ptr[3]                         *(vals + 3)

vals[4]     4          50      *(ptr + 4)
ptr[4]                         *(vals + 4)

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

reference to a pointerऔर pointer to a pointerभीड़

यह एक महान लेख है जो अंतर बताता है और जिसे मैं उद्धृत करूंगा और कुछ कोड चुराऊंगा :)

एक छोटे से उदाहरण के रूप में, यह देखना बहुत मुश्किल हो सकता है कि यदि आप इस तरह से कुछ करना चाहते हैं तो लेखक क्या करना चाहता है:

//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(pvar);
  ....
  return 0;
}

या, कुछ हद तक, कुछ इस तरह:

//function prototype
void func(int** ppInt);

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(&pvar);
  ....
  return 0;
}

तो दिन के अंत में, हम वास्तव में इस सब से क्या हल करेंगे? कुछ भी तो नहीं।

अब हमने ptr-to-ptr और Ref-to-ptr का सिंटैक्स देखा है। क्या एक के बाद एक फायदे हैं? मुझे डर है, नहीं। कुछ प्रोग्रामर के लिए दोनों में से एक का उपयोग सिर्फ व्यक्तिगत प्राथमिकताएं हैं। कुछ लोग जो रेफ-टू-पीटीआर का उपयोग करते हैं, सिंटैक्स "क्लीनर" है, जबकि कुछ लोग जो ptr-to-ptr का उपयोग करते हैं, कहते हैं कि ptr-to-ptr सिंटैक्स उन लोगों के लिए यह स्पष्ट करता है कि आप क्या कर रहे हैं।

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

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

वैसे यह जवाब मुझे उम्मीद से ज्यादा लंबा निकला ...


5
मुझे लगता है कि आपने C C प्रश्न का C ++ उत्तर यहाँ दिया होगा ... कम से कम दूसरा भाग।
dete

पॉइंटर्स टू पॉइंटर्स सी पर लागू होते हैं, साथ ही: पी
डेविड टिटारेंको

1
हे? मैं केवल एरे के आसपास से गुजरते समय पॉइंटर्स टू पॉइंटर्स देखता हूं - आपका दूसरा उदाहरण वास्तव में सबसे सभ्य सी कोड पर लागू नहीं है। इसके अलावा, आप C को C ++ के मेस में खींच रहे हैं - C में संदर्भ मौजूद नहीं हैं।
new123456

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

2
"संदर्भों के संकेत सी में अवैध हैं" - "अनुपस्थित" की तरह अधिक :)
कोस

29

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

इस तथ्य में भी है कि जब सी को शुरू में डिज़ाइन किया गया था, तो यह माना गया था कि आप मशीन आर्किटेक्चर को काफी विस्तृत स्तर पर समझ रहे थे, क्योंकि आपके दिन-प्रतिदिन के काम में इससे बचने का कोई तरीका नहीं था (मेमोरी बहुत तंग थी और प्रोसेसर इतने धीमे थे आपको यह समझना था कि आपने प्रभावित प्रदर्शन को कैसे लिखा है)।


3
हाँ। 'int foo = 5; int * pfoo = & foo; देखें कि यह कितना उपयोगी है? ठीक है, साथ जा रहा हूं ... 'मैंने तब तक पॉइंटर्स का उपयोग नहीं किया जब तक कि मैंने अपनी खुद की डबल-लिस्टेड लाइब्रेरी नहीं लिखी।
जॉन लोपेज

2
+1। मैं CS100 के छात्रों को ट्यूशन करने के लिए उपयोग करता हूं और उनकी कई समस्याओं को केवल समझने वाले तरीके से पॉइंटर्स पर जाकर हल किया गया था।
बेन्ज़ादो

1
ऐतिहासिक संदर्भ के लिए +1। उस समय के लंबे समय बाद शुरू हुआ, मेरे साथ ऐसा कभी नहीं हुआ।
लुमी

26

इस धारणा का समर्थन करने वाला एक शानदार लेख है कि जोएल स्पोल्स्की की साइट पर मुश्किल हैं - द पेरिल्स ऑफ जावास्कूल

[डिस्क्लेमर - मैं प्रति जावा-हैटर ​​नहीं हूं ।]


2
@ जेसन - यह सच है लेकिन तर्क को नकारना नहीं है।
स्टीव टाउनसेंड

4
Spolsky यह नहीं कह रहा है कि JavaSchools ऐसे कारण हैं जो लोगों को पॉइंटर्स मुश्किल लगते हैं। वह कह रहा है कि वे कंप्यूटर विज्ञान की डिग्री वाले पॉइंटर-अनपढ़ लोगों को परिणाम देते हैं।
बेंज़ादो

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

1
@ स्तेव टाउनसेंड: मुझे लगता है कि आप श्री स्पोलस्की के तर्क को याद कर रहे हैं।
जेसन

2
@Steve Townsend: श्री स्पोल्स्की तर्क दे रहा है कि जावा स्कूल ऐसे प्रोग्रामर्स की एक पीढ़ी को खड़ा कर रहे हैं जो पॉइंटर्स और रिकर्सन को नहीं जानते हैं, न कि जावा स्कूलों के प्रचलन के कारण पॉइंटर्स कठिन होते हैं। जैसा कि आपने कहा था "इस बारे में एक महान लेख है कि यह क्यों कठिन है" और कहा गया लेख से जुड़ा हुआ है, ऐसा लगता है कि आपके पास बाद की व्याख्या है। अगर मैं गलत हूं तो मुझे माफ कर देना।
जेसन

24

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

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

अंत में, जब C से मिल रहे हैं, तो संकेत भ्रमित कर रहे हैं कि K & R ने उन्हें समझाने का बहुत अच्छा काम किया है। जिस तरह से मैंने उन्हें सी में सीखा था वह यह जानने के लिए था कि उन्हें कैसे पढ़ना है - दाएं से बाएं। जब मैं int *pअपने सिर में देखता हूं तो मैं कहता हूं " pअंक टू ए int"। सी का आविष्कार विधानसभा भाषा से एक कदम के रूप में किया गया था और यही मुझे इसके बारे में पसंद है - यह उस "ग्राउंड" के करीब है। पॉइंटर्स, कुछ और की तरह, यह समझना मुश्किल है कि क्या आपके पास वह ग्राउंडिंग नहीं है।


1
इसे सीखने का एक अच्छा तरीका 8-बिट माइक्रोकंट्रोलर को प्रोग्राम करना है। उन्हें समझना आसान है। Atmel AVR नियंत्रकों को लें; वे भी जीसीसी द्वारा समर्थित हैं।
Xenu

@Xenu: मैं सहमत हूँ। मेरे लिए यह इंटेल 8008 और 8051 :-)
माइक डनलवे

मेरे लिए यह कस्टम 8-बिट कंप्यूटर ("हो सकता है") समय के मंद क्षणों में एमआईटी में था।
क्वांटमचैनिक

माइक - आपको अपने छात्रों को एक CARDIAC :)
क्वांटमचैनिक

1
@ क्वेंटम: कार्डियाक- अच्छा, ऐसा नहीं सुना था। "हो सकता है" - मुझे लगता है कि, जब ससमान (एट अल) लोगों को मीड-कॉनवे किताब पढ़ रहे थे और अपने स्वयं के एलएसआई चिप्स को फब कर रहे थे? वह मेरे समय के बाद थोड़ा था।
माइक डनलवेई

17

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

अन्यथा, ज्यादातर मैंने जो सोचा था, पृथ्वी पर क्यों आप एक चर चाहते हैं कि आपको मूल्य प्राप्त करने के लिए हुप्स से गुजरना पड़े, और यदि आप इसे सामान सौंपना चाहते थे, तो आपको मूल्यों को प्राप्त करने के लिए अजीब चीजें करनी होंगी उनके अंदर। एक चर का पूरा बिंदु एक मूल्य को संग्रहित करने के लिए कुछ है, मैंने सोचा, इसलिए कोई इसे जटिल बनाना चाहता था वह मेरे से परे था। "तो एक सूचक के साथ आपको *इसके मूल्य पर प्राप्त करने के लिए ऑपरेटर का उपयोग करना होगा ??? यह किस प्रकार का नासमझ चर है?" , मैंने सोचा। व्यर्थ, कोई सज़ा का इरादा नहीं।

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

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


4
यदि आपके पास सीमित रैम (RAM, ROM, CPU) के साथ काम करने के लिए सीमित संसाधन हैं, जैसे कि एम्बेडेड अनुप्रयोगों में, संकेत तेजी से बहुत अधिक समझ में आता है।
निक टी।

निक की टिप्पणी के लिए +1 - विशेष रूप से पासिंग स्ट्रक्चर के लिए।
new123456

12

यहाँ एक पॉइंटर / ऐरे उदाहरण दिया गया है जिसने मुझे विराम दिया। मान लें कि आपके पास दो सरणियाँ हैं:

uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];

और आपका लक्ष्य मेमचपी () का उपयोग करके स्रोत गंतव्य से uint8_t सामग्री की प्रतिलिपि बनाना है। लगता है कि निम्नलिखित में से कौन सा लक्ष्य पूरा होता है:

memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));

जवाब (स्पोइलर अलर्ट!) उनमें से सभी है। "गंतव्य", "और गंतव्य", और "और गंतव्य [0]" सभी समान मूल्य हैं। "गंतव्य" अन्य दो की तुलना में एक अलग प्रकार है, लेकिन यह अभी भी एक ही मूल्य है। वही "स्रोत" के क्रमपरिवर्तन के लिए जाता है।

एक तरफ के रूप में, मैं व्यक्तिगत रूप से पहले संस्करण को पसंद करता हूं।


मैं पहले संस्करण को भी पसंद करता हूं (कम विराम चिह्न को)।
20

++ तो मैं करता हूं, लेकिन आपको वास्तव में सावधान रहने की आवश्यकता है sizeof(source), क्योंकि यदि sourceकोई सूचक है, sizeofतो वह नहीं होगा जो आप चाहते हैं। मैं कभी-कभी (हमेशा नहीं) sizeof(source[0]) * number_of_elements_of_sourceसिर्फ उस बग से दूर रहने के लिए लिखता हूं ।
माइक डनलैवी

गंतव्य, और गंतव्य, और गंतव्य [0] सभी समान नहीं हैं - लेकिन प्रत्येक अलग-अलग तंत्र के माध्यम से एक ही शून्य में परिवर्तित किया जाएगा * जब मेम्ची में उपयोग किया जाता है। हालांकि, जब आकार के तर्क के रूप में उपयोग किया जाता है, तो आपको दो अलग-अलग परिणाम मिलेंगे, और तीन अलग-अलग परिणाम संभव है।
gnasher729

मुझे लगा कि ऑपरेटर के पते की आवश्यकता है?
मार्कस जे।

7

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

सी सीखने पर पहली बात ने मुझे संकेत देने वालों को भ्रमित किया:

char ch;
char str[100];
scanf("%c %s", &ch, str);

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

इस बारे में भ्रमित करने वाली बात यह थी कि &chवास्तव में इसका मतलब क्या था और strइसकी आवश्यकता क्यों नहीं थी।

के बाद मैं इससे परिचित हो गया कि मुझे याद है कि डायनेमिक आवंटन के बारे में उलझन में है। मुझे कुछ बिंदु पर एहसास हुआ कि डेटा के लिए संकेत किसी प्रकार के गतिशील आवंटन के बिना अत्यंत उपयोगी नहीं थे, इसलिए मैंने कुछ ऐसा लिखा:

char * x = NULL;
if (y) {
     char z[100];
     x = z;
}

गतिशील रूप से कुछ स्थान आवंटित करने का प्रयास करें। यह काम नहीं किया। मुझे यकीन नहीं था कि यह काम करेगा, लेकिन मुझे नहीं पता था कि यह कैसे काम कर सकता है।

मुझे बाद में पता चला mallocऔर new, लेकिन वे वास्तव में मेरे लिए जादुई मेमोरी जनरेटर की तरह लग रहे थे। मुझे कुछ नहीं पता था कि वे कैसे काम कर सकते हैं।

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

इस समय के दौरान मुझे यह भी एहसास हुआ कि C में बहुआयामी सरणियों का उपयोग करना बहुत भ्रमित कर सकता है। मुझे पता था कि उन्होंने कैसे काम किया है, लेकिन वे सिर्फ इतना आसान थे कि मैं जब भी मैं उन्हें इस्तेमाल करने के आसपास काम करने की कोशिश करने का फैसला किया। मुझे लगता है कि यहां मुद्दा ज्यादातर वाक्यात्मक (विशेष रूप से कार्य करने या उन्हें वापस करने) से गुजर रहा था।

चूंकि मैं अगले साल के लिए स्कूल के लिए C ++ लिख रहा था या दो मुझे डेटा संरचनाओं के लिए पॉइंटर्स का उपयोग करने का बहुत अनुभव मिला। यहाँ मुझे मुसीबतों का एक नया सेट मिला - पॉइंटर्स मिलाते हुए। मेरे पास कई स्तर के पॉइंटर्स (चीजें जैसे node ***ptr;) मुझे यात्रा करते हैं। मैं एक पॉइंटर को बार-बार गलत संख्या से हटाता हूं और अंततः यह पता लगाने का सहारा लेता *हूं कि मुझे परीक्षण और त्रुटि के लिए कितने की आवश्यकता थी।

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

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

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

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

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

int foo(struct frog * f, int x, int y) {
    struct leg * g = f->left_leg;
    struct toe * t = g->big_toe;
    process(t);

इतना है कि अगर मैं एक पॉइंटर प्रकार को पेंच करता हूं तो यह कंपाइलर त्रुटि से बहुत स्पष्ट है कि समस्या क्या है। अगर मैंने किया:

int foo(struct frog * f, int x, int y) {
    process(f->left_leg->big_toe);

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


1
+1। पूरी तरह से और व्यावहारिक। मैं स्कैनफ़ के बारे में भूल गया था, लेकिन अब जब आप इसे लाएंगे, तो मुझे वही भ्रम होने की याद है।
जो व्हाइट

6

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

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

  2. एक आयामी गतिशील स्मृति आवंटन। 1-डी मेमोरी आवंटन का पता लगाने से मुझे सूचक की अवधारणा को समझने में मदद मिली।

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

  4. स्टैक चर, वैश्विक चर और ढेर स्मृति के बीच अंतर। इन अंतरों का पता लगाने से मुझे स्मृति के प्रकार सिखाए गए हैं जो संकेत / संदर्भ को इंगित करते हैं।

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

अब वापस अपने मूल प्रश्न पर। पिछली सूची के आधार पर, कई आइटम थे जिन्हें मुझे मूल रूप से लोभी करने में कठिनाई थी।

  1. कैसे और क्यों एक सूचक का उपयोग करेगा।
  2. वे कैसे अलग हैं और अभी तक सरणियों के समान हैं।
  3. यह समझना कि पॉइंटर जानकारी कहाँ संग्रहीत है।
  4. सूचक क्या और कहाँ है, यह समझ रहा है।

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

1
@AlexStarbuck - इसका मतलब यह नहीं है कि यह बहुत तेज़ है, लेकिन वैज्ञानिक तरीका एक महान उपकरण है। एक विशेष परिदृश्य के लिए आप क्या सोचते हैं, इसके बारे में एक तस्वीर खुद बनाएं। इसे परीक्षण करने के लिए कुछ प्रोग्राम करें, और जो आपको मिला उसका विश्लेषण करें। क्या यह आपकी अपेक्षा से मेल खाता था? यदि नहीं, तो पहचानें कि कहां भिन्न है? आवश्यकतानुसार दोहराएं, धीरे-धीरे अपनी समझ और मानसिक मॉडल दोनों का परीक्षण करने के लिए जटिलता बढ़ रही है।
स्पार्कली

6

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

उन्हें समझने की कुंजी, मेरे लिए, & (पता) ऑपरेटर थी। एक बार जब मैं समझ गया कि &iइसका अर्थ "मैं का पता" है, तो *iइसका मतलब यह है कि "मेरे द्वारा इंगित पते की सामग्री" थोड़ी देर बाद आई। जब भी मैंने अपना कोड लिखा या पढ़ा तो मैंने हमेशा दोहराया कि "और" का क्या अर्थ है और "*" का क्या अर्थ है और अंततः मैं उन्हें सहज रूप से उपयोग करने के लिए आया था।

मेरी शर्म की बात है, मुझे वीबी और फिर जावा में मजबूर किया गया था, इसलिए मेरा पॉइंटर ज्ञान उतना तेज नहीं है जितना एक बार था, लेकिन मुझे खुशी है कि मैं "पोस्ट-पॉइंटर" हूं। हालाँकि, मुझे एक लाइब्रेरी का उपयोग करने के लिए न कहें, जिसके लिए मुझे * * p को समझना होगा ।


यदि &iपता है और *iसामग्री है, तो क्या है i?
थॉमस अहले

2
मैं i के उपयोग को ओवरलोड कर रहा हूं। एक मनमाना चर i, और i का अर्थ है "मैं का पता, मैं इस पर खुद का मतलब है" की सामग्री "और मैं", और * मेरा मतलब है "एक पते के रूप में और मैं की सामग्री को मानते हैं, उस पते पर जाएं, और वापस पास करें सामग्री "।
गैरी रोवे

5

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

इसलिए मेरे बहुत सीमित अनुभव (कॉलेज में 1 वर्ष की वास्तविक दुनिया + 4) में, मुझे भ्रमित करते हैं क्योंकि मैंने कभी भी कक्षा की सेटिंग के अलावा किसी अन्य चीज़ में इसका उपयोग नहीं किया है। और मैं अब C या C ++ के बजाय JAVA के साथ CS शुरू करने वाले छात्रों के साथ सहानुभूति रख सकता हूं। जैसा कि आपने कहा, आपने 'नवपाषाण' युग में संकेत सीखा है और शायद तब से इसका उपयोग कर रहे हैं। हमारे लिए नए लोगों के लिए, स्मृति को आवंटित करने और सूचक अंकगणितीय करने की धारणा वास्तव में विदेशी है क्योंकि इन सभी भाषाओं में सार है।

PS स्पॉल्स्की निबंध पढ़ने के बाद, 'JavaSchools' का उनका वर्णन कॉर्नेल ('05 -'09) में कॉलेज में जाने से पहले जैसा कुछ नहीं था। मैंने संरचनाओं और कार्यात्मक प्रोग्रामिंग (एसएलएल), ऑपरेटिंग सिस्टम (सी), एल्गोरिदम (पेन और पेपर), और अन्य वर्गों की एक पूरी श्रृंखला ली जो जावा में नहीं सिखाई गई थीं। हालांकि सभी इंट्रो क्लास और ऐच्छिक सभी जावा में किए गए थे क्योंकि पहिया को फिर से मजबूत करने में कोई मूल्य नहीं है जब आप पॉइंटर्स के साथ हैशटेबल लागू करने की तुलना में कुछ उच्चतर स्तर पर करने की कोशिश कर रहे हैं।


4
ईमानदारी से, यह देखते हुए कि आपको पॉइंटर्स के साथ अभी भी कठिनाइयाँ हैं, मुझे यकीन नहीं है कि कॉर्नेल में आपका अनुभव जोएल के लेख के विपरीत है। जाहिर है कि आपका दिमाग अपनी बात मनवाने के लिए जावा-मानसिकता में तार-तार हो गया है।
जकरियन

5
वाट? जावा (या सी #, या पायथन, या संभवतः दर्जनों अन्य भाषाओं) में संदर्भ केवल अंकगणित के बिना संकेत हैं। पॉइंटर्स को समझने का मतलब यह void foo(Clazz obj) { obj = new Clazz(); }है void bar(Clazz obj) { obj.quux = new Quux(); }कि तर्क को

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

1
यह कैसे है कि आप सी में एक धाराप्रवाह बनने के बिना सी में एक ऑपरेटिंग सिस्टम वर्ग के माध्यम से मिला है? कोई अपराध नहीं है, यह सिर्फ मुझे याद है कि बहुत ज्यादा खरोंच से एक सरल ऑपरेटिंग सिस्टम विकसित करना है। मैंने एक हज़ार बार पॉइंटर्स का उपयोग किया होगा ...
गुरुत्वाकर्षण

5

यह एक गैर-उत्तर है: इसका पता लगाने के लिए cdecl (या c ++ घोषणा) का उपयोग करें:

eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int

4

वे सिंटैक्स में एक महत्वपूर्ण बदलाव के बिना कोड में एक अतिरिक्त आयाम जोड़ते हैं। इसके बारे में सोचें:

int a;
a = 5

वहाँ परिवर्तन करने के लिए केवल एक ही बात है: a। आप लिख सकते हैं a = 6और परिणाम अधिकांश लोगों के लिए स्पष्ट हैं। लेकिन अब विचार करें:

int *a;
a = &some_int;

इसके बारे में दो चीजें aअलग-अलग समय पर प्रासंगिक हैं: aसूचक का वास्तविक मूल्य , सूचक और सूचक के पीछे "मूल्य"। आप बदल सकते हैं a:

a = &some_other_int;

... और some_intअभी भी उसी मूल्य के साथ कहीं आसपास है। लेकिन आप इस बात को भी बदल सकते हैं:

*a = 6;

इसके बीच एक वैचारिक अंतर है a = 6, जिसका केवल स्थानीय दुष्प्रभाव है, और *a = 6, जो अन्य स्थानों में अन्य चीजों के एक समूह को प्रभावित कर सकता है। यहाँ मेरा कहना यह नहीं है कि अप्रत्यक्ष की अवधारणा स्वाभाविक रूप से पेचीदा है, लेकिन ऐसा इसलिए है क्योंकि आप दोनों के साथ तात्कालिक, स्थानीय चीज़ aया अप्रत्यक्ष चीज़ *a... ऐसा कर सकते हैं जो लोगों को भ्रमित कर सकता है।


4

मैंने 2 साल की तरह सी ++ में प्रोग्राम किया था और फिर जावा (5 साल) में बदल दिया और फिर कभी पीछे मुड़कर नहीं देखा। हालाँकि, जब मुझे हाल ही में कुछ देशी सामानों का उपयोग करना पड़ा, तो मुझे पता चला (विस्मय के साथ) कि मैं संकेत देने वालों के बारे में कुछ भी नहीं भूल पाया हूँ और मुझे उनका उपयोग करना आसान लगता है। यह एक तीव्र विपरीत है जो मैंने 7 साल पहले अनुभव किया था जब मैंने पहली बार अवधारणा को समझने की कोशिश की थी। तो, मुझे लगता है कि समझ और पसंद प्रोग्रामिंग परिपक्वता का विषय है? :)

या

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

सभी में, समझ में आना मुश्किल है या नहीं, पूरा सूचक विचार बहुत ही शैक्षिक है और मेरा मानना ​​है कि इसे हर प्रोग्रामर को समझना चाहिए, भले ही वह बिंदुओं वाली भाषा पर प्रोग्राम करता हो या नहीं।


3

अप्रत्यक्ष के कारण संकेत कठिन हैं।


"यह कहा जाता है कि कंप्यूटर विज्ञान में कोई समस्या नहीं है जिसे एक स्तर पर अप्रत्यक्ष रूप से हल नहीं किया जा सकता है" (कोई विचार नहीं है जिसने इसे पहले कहा था, हालांकि)
द आर्केटीपल पॉल

यह जादू की तरह है, जहां गलतफहमी लोगों को भ्रमित करती है (लेकिन पूरी तरह से भयानक है)
Nick T

3

पॉइंटर्स (कम-स्तर के काम के कुछ अन्य पहलुओं के साथ), उपयोगकर्ता को जादू को दूर करने की आवश्यकता होती है।

अधिकांश उच्च स्तरीय प्रोग्रामर जादू की तरह।


3

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

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

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

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

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


3

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

सी में, पॉइंटर्स का उपयोग अन्य चीजों के लिए किया जाता है:

  • पुनरावर्ती डेटा संरचनाओं को परिभाषित करें

C में आप पूर्णांक की एक सूची को इस तरह परिभाषित करेंगे:

struct node {
  int value;
  struct node* next;
}

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

data List = List Int List | Null

बहुत सीधा - एक सूची या तो खाली है, या एक मूल्य और बाकी सूची से बनाई गई है।

  • तार और सरणियों पर Iterate

यहां बताया गया है कि आप fooC में स्ट्रिंग के प्रत्येक वर्ण पर एक फ़ंक्शन कैसे लागू कर सकते हैं :

char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }

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

  • बहुरूपता को प्राप्त करें

यहाँ एक वास्तविक फ़ंक्शन हस्ताक्षर glib में पाया गया है :

typedef struct g_list GList;

void  g_list_foreach    (GList *list,
                 void (*func)(void *data, void *user_data),
                         void* user_data);

वाह! वह काफी मुंहफट है void*। और यह सब केवल एक फ़ंक्शन को घोषित करने के लिए है जो एक सूची पर पुनरावृत्त करता है जिसमें किसी भी प्रकार की चीज़ हो सकती है, प्रत्येक सदस्य के लिए फ़ंक्शन लागू करना। mapहास्केल में कैसे घोषित किया जाता है, इसकी तुलना करें :

map::(a->b)->[a]->[b]

यह बहुत अधिक सीधा है: mapएक ऐसा फंक्शन है जो एक फंक्शन लेता है जो aa को कंवर्ट करता है b, और इसे a's' की लिस्ट बनाने के लिए लागू करता है b। सी फ़ंक्शन की तरह g_list_foreach, mapइसे लागू करने के प्रकारों के बारे में अपनी परिभाषा में कुछ भी जानने की जरूरत नहीं है।

सारांश में:

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


c != NULLअपने "नमस्ते दुनिया" उदाहरण में दुरुपयोग ... आपका मतलब है *c != '\0'
ओलाफ सीबार्ट

2

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

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


2
ग्रॉकिंग पॉइंटर्स को मशीन कोड या असेंबली की समझ की आवश्यकता नहीं होती है।
जेसन

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

2

मेरे पास जो समस्या हमेशा रही है (मुख्य रूप से स्व-सिखाया गया है) एक पॉइंटर का उपयोग करने के लिए "जब" है। मैं पॉइंटर बनाने के लिए सिंटैक्स के चारों ओर अपना सिर लपेट सकता हूं लेकिन मुझे यह जानना होगा कि किन परिस्थितियों में पॉइंटर का उपयोग किया जाना चाहिए।

क्या मैं इस मानसिकता वाला एकमात्र व्यक्ति हूं? ;-)


मै समझ गया। मेरी प्रतिक्रिया थोड़े के साथ है।
जे। पोलफर

2

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


2

ऐसा लगता है कि कई छात्रों को अप्रत्यक्ष की अवधारणा के साथ एक समस्या है, खासकर जब वे पहली बार अप्रत्यक्ष की अवधारणा से मिलते हैं। मुझे याद है जब मैं एक छात्र था जो मेरे पाठ्यक्रम के +100 छात्रों में से केवल कुछ ही लोग वास्तव में संकेत समझ रहे थे।

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


2

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

मुझे जो रास्ता मिला वह यही था। कल्पना कीजिए कि सभी परिभाषित चर संकलन समय (स्टैक पर) में मेमोरी स्पेस दिए गए हैं। यदि आप ऐसा प्रोग्राम चाहते हैं जो बड़ी डेटा फ़ाइलों जैसे ऑडियो या छवियों को संभाल सके, तो आप इन संभावित संरचनाओं के लिए निश्चित मात्रा में मेमोरी नहीं चाहेंगे। तो आप इस डेटा (ढेर पर) को रखने के लिए एक निश्चित मात्रा में मेमोरी असाइन करने के लिए रनटाइम तक इंतजार करते हैं।

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

कम से कम इस समय मैं इसे देख रहा हूँ!


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

1

C ++ newbie के रूप में यहां बोल रहा हूं:

पॉइंटर सिस्टम ने मुझे अवधारणा के कारण जरूरी नहीं पचाने में कुछ समय लिया, लेकिन जावा के सापेक्ष सी ++ सिंटैक्स के कारण। कुछ चीजें जो मुझे भ्रामक लगीं वो हैं:

(1) परिवर्तनीय घोषणा:

A a(1);

बनाम

A a = A(1);

बनाम

A* a = new A(1); 

और स्पष्ट रूप से

A a(); 

एक फ़ंक्शन घोषणा है और एक चर घोषणा नहीं है। अन्य भाषाओं में, मूल रूप से एक चर घोषित करने का सिर्फ एक तरीका है।

(२) एम्परसेंड का उपयोग कुछ अलग तरीकों से किया जाता है। अगर यह होता है

int* i = &a;

तब & a एक मेमोरी एड्रेस है।

OTOH, यदि यह है

void f(int &a) {}

तब & a एक पारित-दर-संदर्भ पैरामीटर है।

यद्यपि यह तुच्छ लग सकता है, यह नए उपयोगकर्ताओं के लिए भ्रामक हो सकता है - मैं जावा और जावा की भाषा से आया हूं जो ऑपरेटरों के अधिक समान उपयोग के साथ एक भाषा है

(३) एरे-पॉइंटर रिलेशनशिप

एक बात यह है कि एक बालक को समझने के लिए निराशा होती है कि एक सूचक है

int* i

एक इंट का सूचक हो सकता है

int *i = &n; // 

या

एक int के लिए एक सरणी हो सकती है

int* i = new int[5];

और फिर बस चीजों को गड़बड़ करने के लिए, पॉइंटर्स और सरणी सभी मामलों में विनिमेय नहीं हैं और पॉइंटर्स को पैरामीटर के रूप में पारित नहीं किया जा सकता है।

यह कुछ बुनियादी कुंठाओं के बारे में बताता है जो मैंने C / C ++ और इसके संकेतकर्ताओं के साथ की थी, जो IMO, इस तथ्य से बहुत जटिल है कि C / C ++ में ये सभी भाषा-विशिष्ट प्रश्न हैं।


C ++ 2011 ने चीजों में काफी सुधार किया है जहां तक ​​चर घोषणाओं का संबंध है।
gnasher729

0

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


0

संकेत .. ह्ह .. मेरे सिर में सूचक के बारे में सब यह है कि यह एक स्मृति पता देता है जहां इसके संदर्भ का वास्तविक मूल्य है .. इसलिए इसके बारे में कोई जादू नहीं है .. अगर आप कुछ विधानसभा सीखते हैं तो आपको सीखने में बहुत परेशानी नहीं होगी कैसे संकेत काम करता है .. दोस्तों पर आते हैं ... यहां तक ​​कि जावा में भी सब कुछ एक संदर्भ है


0

मुख्य समस्या लोगों को यह समझ में नहीं आता है कि उन्हें पॉइंटर्स की आवश्यकता क्यों है। क्योंकि वे ढेर और ढेर के बारे में स्पष्ट नहीं हैं। छोटे मेमोरी मोड के साथ x86 के लिए 16bit कोडांतरक से शुरू करना अच्छा है। इसने ढेर, ढेर और "पता" का विचार प्राप्त करने में कई लोगों की मदद की। और बाइट :) आधुनिक प्रोग्रामर कभी-कभी आपको यह नहीं बता सकते हैं कि 32 बिट स्पेस को संबोधित करने के लिए आपको कितने बाइट्स की आवश्यकता है। वे संकेत का विचार कैसे प्राप्त कर सकते हैं?

दूसरा क्षण संकेतन है: आप सूचक को * घोषित करते हैं, आपको पते के रूप में & मिलता है और यह कुछ लोगों के लिए समझना आसान नहीं है।

और आखिरी चीज जो मैंने देखी, वह थी स्टोरेज समस्या: वे ढेर और स्टैक को समझते हैं लेकिन "स्टैटिक" के विचार में नहीं आ सकते।

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