पॉइंटर्स का उपयोग क्यों करें? [बन्द है]


356

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

मूल रूप से मेरे तीन प्रश्न हैं:

  1. सामान्य चर पर पॉइंटर्स का उपयोग क्यों करें?
  2. मुझे कब और कहाँ पॉइंटर्स का उपयोग करना चाहिए?
  3. आप सरणियों के साथ पॉइंटर्स का उपयोग कैसे करते हैं?

5
इस सवाल पर पहले चर्चा की गई आशा है कि मदद करता है!
डग टी।

4
पुस्तकों की सूची के लिए, stackoverflow.com/questions/388242/… देखें । जावा के बाद, मैंने त्वरित C ​​++ को बहुत उपयोगी पाया ।
जब्बा

163
हम पॉइंटर्स का उपयोग करते हैं क्योंकि किसी को अपने घर का पता देने से ज्यादा आसान है कि आप सभी को अपने घर की एक प्रति दें।
ऋषि दुआ

14
@ ऋषिदुआ एक पॉइंटर का सबसे अच्छा स्पष्टीकरण है जो मैंने कभी भी देखा है। इसके लिए धन्यवाद, इसने मेरी समझ को बढ़ा दिया है :)
जोश

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

जवाबों:


172
  • सामान्य चर पर पॉइंटर्स का उपयोग क्यों करें?

संक्षिप्त उत्तर है: नहीं। ;-) पॉइंटर्स का उपयोग किया जाना है जहाँ आप कुछ और उपयोग नहीं कर सकते हैं। यह या तो इसलिए है क्योंकि उपयुक्त कार्यक्षमता की कमी, लापता डेटा प्रकार या शुद्ध पूर्णता के लिए। अधिक नीचे ...

  • मुझे कब और कहाँ पॉइंटर्स का उपयोग करना चाहिए?

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

  • आप सरणियों के साथ पॉइंटर्स का उपयोग कैसे करते हैं?

थोड़े प्रयास और बहुत भ्रम के साथ। ;-) अगर हम सरल डेटा प्रकारों के बारे में बात करते हैं जैसे कि इंट और चार एक सरणी और एक पॉइंटर के बीच थोड़ा अंतर है। ये घोषणाएँ बहुत समान हैं (लेकिन समान नहीं हैं - जैसे, sizeofअलग-अलग मान लौटाएंगे):

char* a = "Hello";
char a[] = "Hello";

आप इस तरह से सरणी में किसी भी तत्व तक पहुँच सकते हैं

printf("Second char is: %c", a[1]);

सूचकांक 1 के बाद से सरणी तत्व 0. से शुरू होता है। :-)

या आप समान रूप से ऐसा कर सकते थे

printf("Second char is: %c", *(a+1));

पॉइंटर ऑपरेटर (*) की जरूरत है क्योंकि हम प्रिंटफ को बता रहे हैं कि हम एक चरित्र प्रिंट करना चाहते हैं। * के बिना, स्मृति पते का वर्ण प्रतिनिधित्व स्वयं मुद्रित किया जाएगा। अब हम इसके बजाय चरित्र का ही उपयोग कर रहे हैं। यदि हमने% c के बजाय% s का उपयोग किया है, तो हमने प्रिंटफ को 'प्लस ए' (ऊपर के उदाहरण में) द्वारा निर्दिष्ट मेमोरी एड्रेस की सामग्री को प्रिंट करने के लिए कहा होगा, और हमें * नहीं डालना होगा सामने:

printf("Second char is: %s", (a+1)); /* WRONG */

लेकिन यह सिर्फ दूसरे वर्ण को नहीं छापता था, बल्कि अगले स्मृति पतों में सभी वर्णों को, जब तक कि एक अशक्त चरित्र (\ 0) नहीं मिला होता। और यहीं से चीजें खतरनाक होने लगती हैं। क्या होगा यदि आप% s फॉर्मैटर के साथ गलती से एक चर पॉइंटर के बजाय टाइप पूर्णांक के एक चर को प्रिंट करते हैं?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

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

ओह, और अगर आप इसे घोषित करते समय चार सरणी / पॉइंटर को एक स्ट्रिंग मान असाइन नहीं करते हैं, तो आप इसे मूल्य देने से पहले इसे पर्याप्त मात्रा में मेमोरी आवंटित करें। मॉलॉक, कॉलोक या इसी तरह का उपयोग करना। चूँकि आपने केवल अपने सरणी में एक तत्व / एक एकल मेमोरी पते को इंगित करने के लिए घोषित किया है। तो यहाँ कुछ उदाहरण हैं:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

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


8
इसमें 120 के साथ आपका उदाहरण गलत है। यह% c नहीं% s का उपयोग कर रहा है इसलिए कोई बग नहीं है; यह केवल निचले अक्षर x को प्रिंट करता है। इसके अलावा, आपके बाद का दावा है कि एक सूचक प्रकार int गलत है और संकेत के साथ अनुभवहीन सी प्रोग्रामर को देने के लिए बहुत बुरी सलाह है।
R .. गिटहब स्टॉप हेल्पिंग ICE

13
-1 आपने अच्छी शुरुआत की लेकिन पहला उदाहरण गलत है। नहीं, ऐसा नहीं है। पहले मामले aमें एक सूचक है और दूसरे मामले aमें एक सरणी है। क्या मैंने इसका जिक्र पहले ही कर दिया था? यह वैसा नहीं है! अपने लिए जाँच करें: साइज़ोफ़ (ए) की तुलना करें, एक नए पते को एक सरणी पर असाइन करने का प्रयास करें। यह काम नहीं करेगा।
सिटिबिट्ज़

42
char* a = "Hello";और char a[] = "Hello";समान नहीं हैं, वे काफी अलग हैं। एक एक सूचक को दूसरे को एक सरणी घोषित करता है। एक कोशिश करो sizeofऔर तुम differnce देखते हैं।
पैट्रिक श्ल्टर

12
"यह प्रिंटफ़ स्टेटमेंट निष्पादित करना गलत या अवैध नहीं है"। यह सादा गलत है। उस प्रिंटफ स्टेटमेंट में अपरिभाषित व्यवहार है। कई कार्यान्वयनों पर यह एक पहुंच उल्लंघन का कारण बनेगा। पॉइंटर्स वास्तव में टाइप इंट के नहीं हैं, वे वास्तव में पॉइंटर्स (और कुछ नहीं) हैं।
3

19
-1, आपने यहां बहुत सारे तथ्यों को गलत बताया है और मैं कहूंगा कि आप अपवोट या स्वीकृत उत्तर स्थिति के लायक नहीं हैं।
आसवीकाऊ

50

पॉइंटर्स का उपयोग करने का एक कारण यह है कि एक चर या ऑब्जेक्ट को एक फ़ंक्शन में संशोधित किया जा सकता है।

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

निम्नलिखित उदाहरणों पर विचार करें:

संदर्भ का उपयोग करना:

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

संकेत का उपयोग करना:

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}

16
संभवतः यह उल्लेख करने के लिए एक अच्छा विचार है कि संदर्भ अधिक सुरक्षित हैं, इसमें आप एक अशक्त संदर्भ नहीं दे सकते।
स्पूनमाइजर

26
हाँ, यह शायद संदर्भों का उपयोग करने का सबसे बड़ा लाभ है। इस पर ध्यान दिलाने के लिए धन्यवाद। कोई
सज़ा का

2
आप निश्चित रूप से अशक्त संदर्भ पारित कर सकते हैं। यह अशक्त पॉइंटर पास करना उतना आसान नहीं है।
n0rd

1
@ n0rd के साथ कॉन्सुर। अगर आपको लगता है कि आप एक गलत संदर्भ नहीं दे सकते हैं तो आप गलत हैं। अशक्त संदर्भ की तुलना में झूलते संदर्भ को पारित करना आसान है, लेकिन अंततः आप या तो आसानी से पर्याप्त कर सकते हैं। संदर्भ कोई चांदी की गोली नहीं है जो एक इंजीनियर को पैर में खुद को गोली मारने से बचाती है। इसे लाइव देखें
WhozCraig

2
@ n0rd: ऐसा करना जो स्पष्ट रूप से अपरिभाषित व्यवहार है।
डैनियल कामिल कोजार

42
  1. संकेत आपको कई स्थानों से स्मृति में एक ही स्थान को संदर्भित करने की अनुमति देते हैं। इसका मतलब है कि आप मेमोरी को एक स्थान पर अपडेट कर सकते हैं और आपके प्रोग्राम में किसी अन्य स्थान से परिवर्तन देखा जा सकता है। आप अपने डेटा संरचनाओं में घटकों को साझा करने में सक्षम होने से अंतरिक्ष को भी बचाएंगे।
  2. आपको किसी भी स्थान पर पॉइंटर्स का उपयोग करना चाहिए जहां आपको मेमोरी में किसी विशिष्ट स्थान पर पते के आसपास प्राप्त करने और पास करने की आवश्यकता होती है। आप सरणियों को नेविगेट करने के लिए पॉइंटर्स का भी उपयोग कर सकते हैं:
  3. एक सरणी सन्निहित मेमोरी का एक ब्लॉक है जिसे एक विशिष्ट प्रकार के साथ आवंटित किया गया है। सरणी के नाम में सरणी के आरंभिक स्थान का मान होता है। जब आप 1 जोड़ते हैं, तो यह आपको दूसरे स्थान पर ले जाता है। यह आपको उन पंक्तियों को लिखने की अनुमति देता है जो एक सूचक को बढ़ाता है जो सरणी तक पहुंचने में उपयोग के लिए एक स्पष्ट काउंटर के बिना सरणी को स्लाइड करता है।

यहाँ C में एक उदाहरण दिया गया है:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

प्रिंट

ifmmp

क्योंकि यह प्रत्येक वर्ण के लिए मूल्य लेता है और एक-एक करके इसे बढ़ाता है।


because it takes the value for each character and increments it by one। आस्की प्रतिनिधित्व में है या कैसे है?
एडुआर्डो एस।

28

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

पॉइंटर्स और पॉइंटर अंकगणित का सबसे अच्छा विवरण जो मैंने पढ़ा है वह K & R की सी प्रोग्रामिंग भाषा में है । C ++ सीखने के लिए एक अच्छी पुस्तक C ++ प्राइमर है


1
धन्यवाद! अंत में, स्टैक का उपयोग करने के लाभों का एक व्यावहारिक स्पष्टीकरण! एक सरणी में स्थिति के लिए एक पॉइंटर भी पॉइंटर @ और सापेक्ष के उपयोग से प्रदर्शन में सुधार करता है?

यह शायद मैंने पढ़ा सबसे अच्छा स्पष्टीकरण है। लोगों के पास "अति जटिल" चीजों का एक तरीका है :)
डेटिलियम

23

मुझे भी इसका उत्तर देने का प्रयास करें।

संकेत संदर्भों के समान हैं। दूसरे शब्दों में, वे प्रतियां नहीं हैं, बल्कि मूल मूल्य को संदर्भित करने का एक तरीका है।

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

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

अब अगर आप मूल मूल्य को संशोधित करना चाहते हैं तो क्या होगा? आप कुछ इस तरह का उपयोग कर सकते हैं:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

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

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

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

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

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

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

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

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

बस इस अंतर को याद रखें: एक संदर्भ अनिवार्य रूप से एक वैध सूचक है। एक सूचक हमेशा मान्य नहीं होता है।

तो क्या मैं यह कह रहा हूं कि इसका एक अमान्य संदर्भ बनाना असंभव है? नहीं, यह पूरी तरह से संभव है, क्योंकि C ++ आपको लगभग कुछ भी करने देता है। यह सिर्फ अनायास ही करना कठिन है और आप इस बात से चकित होंगे कि कितने कीड़े अनजाने में हैं :)


आप मेमोरी-मैप किए गए IO के लिए अच्छे रैपर कक्षाएं लिख सकते हैं जिसके साथ आप अनिवार्य रूप से पॉइंटर्स के उपयोग से बच सकते हैं।
ईनपोकलम

13

यहाँ थोड़ा अलग है, लेकिन C की कई विशेषताएं समझ में नहीं आती हैं: http://steve.yegge.googlepages.com/tour-de-babel#C

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

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


12

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

DOS के पुराने दिनों में, आप पॉइंटर की घोषणा करके सीधे वीडियो कार्ड की वीडियो मेमोरी तक पहुंचने में सक्षम होते थे:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

कई एम्बेडेड डिवाइस अभी भी इस तकनीक का उपयोग करते हैं।


एक सूचक का उपयोग करने का कारण नहीं - एक स्पैन जैसी संरचना - जो लंबाई भी रखती है - बहुत अधिक उपयुक्त है। इन दिनों यह है gsl::span, और जल्द ही यह होगा std::span
ईनपोकलम

10

बड़े हिस्से में, पॉइंटर्स एरेज़ (सी / सी ++ में) हैं - वे मेमोरी में पते हैं, और अगर वांछित ("सामान्य" मामलों में) एक सरणी की तरह एक्सेस किया जा सकता है।

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

यदि आप डायनेमिक स्टोरेज आवंटन (जैसे लिंक्ड-लिस्ट के लिए) करना चाहते हैं, तो आपको पॉइंटर्स का उपयोग करना होगा, क्योंकि वे हीप से मेमोरी को हथियाने का एकमात्र तरीका है।


8
मुझे लगता है कि यह कहना भ्रामक है कि संकेत सरणियाँ हैं। वास्तव में, सरणी नाम सरणी के पहले तत्व में कॉन्स्ट पॉइंटर्स हैं। सिर्फ इसलिए कि आप एक मनमाना बिंदु तक पहुँच सकते हैं जैसे कि एक सरणी का मतलब यह नहीं है ... आपको एक एक्सेस उल्लंघन मिल सकता है :)
rmeador

2
संकेत सरणियाँ नहीं हैं। Comp.lang.c के अक्सर पूछे जाने वाले प्रश्न अनुभाग 6 को पढ़ें ।
कीथ थॉम्पसन

पॉइंटर्स वास्तव में एरेज़ नहीं हैं। इसके अलावा, कच्चे सरणियों में बहुत अधिक उपयोग नहीं है - निश्चित रूप से सी ++ 11 के बाद और नहीं std::array
ईनपोकलम

@einpoklum - पॉइंटर्स संदर्भ कार्यों में बराबर होने के लिए सरणियों के काफी करीब हैं और सरणियों के माध्यम से पुनरावृत्ति करते हैं :) .... यह भी - C ++ 11 रिलीज से 3 साल दूर था जब यह जवाब 2008 में लिखा गया था
वॉरेन

@warren: संकेत सरणियाँ नहीं हैं और न ही सरणियों के करीब हैं, वे केवल सरणियों में क्षय करते हैं। लोगों को यह बताना अनुचित है कि वे समान हैं। इसके अलावा, आपके पास निश्चित रूप से सरणियों के संदर्भ हो सकते हैं, जो एक ही प्रकार के संकेत नहीं हैं और आकार की जानकारी बनाए रखते हैं; यहाँ
einpoklum

9

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

पॉइंटर्स उपयोगी हैं जहां आपको उच्च प्रदर्शन और / या कॉम्पैक्ट मेमोरी फुटप्रिंट की आवश्यकता होती है।

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


9

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


यह संदर्भों का उपयोग करने का एक कारण है (या अधिकांश - संदर्भ आवरण), न कि संकेत।
ईनपोकलम

6

C ++ में, आप उप-प्रकार उपयोग करना चाहते हैं बहुरूपता , आप है संकेत का उपयोग करें। इस पोस्ट को देखें: बिंदुओं के बिना C ++ बहुरूपता

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

एक वैरिएबल क्लास का ऑब्जेक्ट रखने वाले वेरिएबल का यह विचार स्टैक पर ऑब्जेक्ट्स को स्टोर करने के लिए C ++ के डिफॉल्ट (नॉन-पॉइंटर) मोड के साथ असंगत है, जहां आवंटित की गई स्पेस की मात्रा सीधे क्लास से मेल खाती है। नोट: यदि किसी वर्ग में 5 उदाहरण फ़ील्ड बनाम 3 हैं, तो अधिक स्थान आवंटित करने की आवश्यकता होगी।


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


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

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

यदि आपके पास एक व्युत्पन्न वर्ग के आधार वर्ग का संदर्भ है और उस संदर्भ में एक आभासी विधि कहते हैं, तो व्युत्पन्न वर्ग का ओवरराइड कहा जाएगा। क्या मै गलत हु?
ईनपोकलम

@ einpoklum-reinstateMonica यह सही है। लेकिन आप यह नहीं बदल सकते कि किस वस्तु को संदर्भित किया गया है। इसलिए यदि आप उन वस्तुओं की सूची / सेट / सरणी पर पाशन कर रहे हैं, तो एक संदर्भ चर काम नहीं करेगा।
सिल्डोरेथ

5

क्योंकि सभी स्थानों पर बड़ी वस्तुओं की नकल करना समय और स्मृति को बर्बाद करता है।


4
और संकेत कैसे मदद करते हैं? मुझे लगता है कि जावा या .net से आने वाला व्यक्ति स्टैक और हीप के बीच अंतर नहीं जानता है, इसलिए यह उत्तर बहुत ही बेकार है ..
मैट फ्रेडरिकसन

आप संदर्भ से गुजर सकते हैं। जिससे नकल पर रोक लगेगी।
मार्टिन यॉर्क

1
@MatsFredriksson - एक बड़ी डेटा संरचना को पारित करने (कॉपी करने) और परिणाम को फिर से कॉपी करने के बजाय, आप बस यह इंगित करते हैं कि यह रैम में कहां है और फिर इसे सीधे संशोधित करें।
जॉन यू

इसलिए बड़ी वस्तुओं की नकल न करें। किसी ने नहीं कहा कि आपको इसके लिए संकेत की आवश्यकता है।
ईनपोकलम

5

यहाँ मेरी awser है, और मैं एक विशेषज्ञ बनने का वादा नहीं करूँगा, लेकिन मैंने पाया है कि मैं अपने एक पुस्तकालय में लिखने के लिए कोशिश कर रहा हूँ। इस लाइब्रेरी में (यह OpenGL :-) के साथ एक ग्राफिक्स एपीआई है) आप उनमें से गुजरने वाली शीर्ष वस्तुओं के साथ एक त्रिकोण बना सकते हैं। ड्रा विधि इन त्रिभुज वस्तुओं को ले जाती है, और अच्छी तरह से .. उन्हें मेरे द्वारा बनाई गई शीर्षस्थ वस्तुओं के आधार पर खींचती है। खैर यह ठीक है।

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

अब, क्या होगा यदि मेरे पास एक जाली है जिसमें कई कोने और टन के त्रिकोण हैं, और मेष घूम रहा है, और चल रहा है, और ऐसे। मुझे हर त्रिभुज को अपडेट करना होगा जो इन शीर्षों का उपयोग करता है, और शायद दृश्य में प्रत्येक त्रिकोण क्योंकि मुझे नहीं पता होगा कि कौन से कोने का उपयोग करते हैं। यह बेहद कंप्यूटर गहन है, और अगर मेरे पास एक परिदृश्य के कई जाल हैं, हे भगवान! मैं मुसीबत में हूँ, क्योंकि हर त्रिकोण को लगभग हर फ्रेम में अपडेट कर रहा हूँ क्योंकि ये कोने समय बदल रहे हैं!

संकेत के साथ, आपको त्रिकोणों को अपडेट करने की आवश्यकता नहीं है।

यदि मेरे पास त्रिभुज वर्ग की तीन * वर्टेक्स वस्तुएं हैं, तो न केवल मैं कमरे की बचत कर रहा हूं क्योंकि एक ज़िलियन त्रिकोण में तीन वर्टेक्स ऑब्जेक्ट नहीं होते हैं जो स्वयं बड़े होते हैं, लेकिन ये संकेत हमेशा उन ऊर्ध्वाधर बिंदुओं के लिए भी होंगे जो वे करने के लिए होते हैं, कोई फर्क नहीं पड़ता कितनी बार कोने बदलते हैं। चूँकि संकेत अभी भी एक ही शीर्ष पर स्थित हैं, त्रिकोण नहीं बदलते हैं, और अद्यतन प्रक्रिया को संभालना आसान है। अगर मैं आपको भ्रमित करता हूं, तो मुझे संदेह नहीं होगा, मैं एक विशेषज्ञ होने का नाटक नहीं करता, सिर्फ अपने दो सेंट को चर्चा में फेंक देता हूं।


4

सी भाषा में पॉइंटर्स की आवश्यकता यहां वर्णित है

मूल विचार यह है कि डेटा की मेमोरी स्थानों के साथ हेरफेर करके भाषा में कई सीमाएं (जैसे एरेज़, स्ट्रिंग्स का उपयोग करना और फ़ंक्शन में कई चर को संशोधित करना) को हटाया जा सकता है। इन सीमाओं को पार करने के लिए, संकेत सी में पेश किए गए थे।

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

पुनश्च: कृपया नमूना कोड के साथ विस्तृत विवरण के लिए दिए गए लिंक को देखें ।


प्रश्न C ++ के बारे में है, यह उत्तर C
einpoklum

नहींं, प्रश्न को c और c ++ टैग किया गया है। हो सकता है कि सी टैग अप्रासंगिक हो, लेकिन यह यहां शुरू से है।
जीन-फ्रांकोइस फेबरे

3

जावा और C # में सभी ऑब्जेक्ट रेफरेंस पॉइंटर्स हैं, c ++ वाली बात यह है कि आपके पास पॉइंटर्स पॉइंट्स पर आपका अधिक नियंत्रण है। याद रखें महान शक्ति के साथ भव्य जिम्मेदारी आती है।


1

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

C ++ के साथ समस्या यह है कि लोग आम तौर पर पॉइंटर्स को बदलने के लिए उपयोग करते हैं, उस टूलसेट पर बहुत निर्भर करते हैं जिसका आप उपयोग करते हैं जो तब ठीक होता है जब आपके पास सोर्स कोड पर आपके लिए आवश्यक सभी नियंत्रण होते हैं, हालांकि यदि आप दृश्य स्टूडियो 2008 के साथ एक स्थिर लाइब्रेरी संकलित करते हैं उदाहरण के लिए और इसे एक दृश्य स्टूडियो 2010 में उपयोग करने का प्रयास करें, आपको एक टन लिंकर त्रुटियां मिलेंगी क्योंकि नई परियोजना एसटीएल के नए संस्करण से जुड़ी हुई है जो पीछे की ओर संगत नहीं है। अगर आप एक DLL संकलित करते हैं और लोग एक अलग टूलसेट में उपयोग करते हैं, तो चीजें और भी नस्टियर हो जाती हैं, क्योंकि उस स्थिति में आपका प्रोग्राम जल्द या बाद में बिना किसी स्पष्ट कारण के क्रैश हो जाएगा।

तो बड़े डेटा सेट को एक लाइब्रेरी से दूसरे में ले जाने के उद्देश्य से आप फ़ंक्शन को एक व्यूअर में एक पॉइंटर देने पर विचार कर सकते हैं जो कि डेटा को कॉपी करने के लिए माना जाता है यदि आप दूसरों को उसी टूल का उपयोग करने के लिए मजबूर नहीं करना चाहते हैं जो आप उपयोग करते हैं । इसके बारे में अच्छी बात यह है कि इसमें सी-स्टाइल ऐरे भी नहीं है, आप उदाहरण के लिए पहले तत्व और वेक्टर [0] का पता देकर एक std :: वेक्टर का उपयोग कर सकते हैं और पॉइंटर दे सकते हैं। एसटीडी :: वेक्टर आंतरिक रूप से सरणी का प्रबंधन करने के लिए।

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


1
  • कुछ मामलों में, फ़ंक्शन पॉइंटर्स को उन कार्यों का उपयोग करने की आवश्यकता होती है जो एक साझा पुस्तकालय (.DLL या .so) में हैं। इसमें संपूर्ण भाषाओं में प्रदर्शन करने वाले सामान शामिल हैं, जहाँ अक्सर DLL इंटरफ़ेस प्रदान किया जाता है।
  • कंपाइलर बनाना
  • वैज्ञानिक कैलकुलेटर बनाना, जहां आपके पास फ़ंक्शन पॉइंटर्स की एक सरणी या वेक्टर या स्ट्रिंग मानचित्र है?
  • सीधे वीडियो मेमोरी को संशोधित करने की कोशिश कर रहा है - अपने खुद के ग्राफिक्स पैकेज बना रहा है
  • एक एपीआई बनाना!
  • डेटा संरचना - आपके द्वारा बनाए जा रहे विशेष पेड़ों के लिए नोड लिंक पॉइंटर्स

संकेत के कारणों के बहुत सारे हैं। यदि आप क्रॉस-लैंग्वेज कम्पैटिबिलिटी बनाए रखना चाहते हैं तो सी नाम का मेलिंग करना विशेष रूप से डीएलएल में महत्वपूर्ण है।

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