दोहरा अप्रत्यक्ष उपयोग क्यों करें? या पॉइंटर्स टू पॉइंटर्स का उपयोग क्यों करें?


272

C में दोहरा अप्रत्यक्ष उपयोग कब किया जाना चाहिए? क्या कोई उदाहरण के साथ समझा सकता है?

मुझे क्या पता है कि एक डबल अप्रत्यक्ष एक सूचक के लिए एक सूचक है। मुझे एक पॉइंटर को पॉइंटर की आवश्यकता क्यों होगी?


49
सावधान रहे; वाक्यांश "डबल पॉइंटर" भी प्रकार को संदर्भित करता है double*
कीथ थॉम्पसन

ध्यान दें: इस प्रश्न का उत्तर C और C ++ के लिए अलग है - इस बहुत पुराने प्रश्न में c + टैग न जोड़ें।
भाग

जवाबों:


479

यदि आप वर्णों की एक सूची (एक शब्द) चाहते हैं, तो आप उपयोग कर सकते हैं char *word

यदि आप शब्दों की एक सूची (एक वाक्य) चाहते हैं, तो आप उपयोग कर सकते हैं char **sentence

यदि आप वाक्यों की सूची (एक एकालाप) चाहते हैं, तो आप उपयोग कर सकते हैं char ***monologue

यदि आप मोनोलॉग (एक जीवनी) की सूची चाहते हैं, तो आप उपयोग कर सकते हैं char ****biography

यदि आप आत्मकथाओं (एक जैव-पुस्तकालय) की सूची चाहते हैं, तो आप उपयोग कर सकते हैं char *****biolibrary

यदि आप जैव-पुस्तकालयों (एक ?? लोल) की सूची चाहते हैं, तो आप उपयोग कर सकते हैं char ******lol

... ...

हाँ, मुझे पता है कि ये सर्वोत्तम डेटा संरचनाएँ नहीं हो सकती हैं


बहुत बहुत उबाऊ लोल के साथ उदाहरण का उपयोग करें

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

int wordsinsentence(char **x) {
    int w = 0;
    while (*x) {
        w += 1;
        x++;
    }
    return w;
}

int wordsinmono(char ***x) {
    int w = 0;
    while (*x) {
        w += wordsinsentence(*x);
        x++;
    }
    return w;
}

int wordsinbio(char ****x) {
    int w = 0;
    while (*x) {
        w += wordsinmono(*x);
        x++;
    }
    return w;
}

int wordsinlib(char *****x) {
    int w = 0;
    while (*x) {
        w += wordsinbio(*x);
        x++;
    }
    return w;
}

int wordsinlol(char ******x) {
    int w = 0;
    while (*x) {
        w += wordsinlib(*x);
        x++;
    }
    return w;
}

int main(void) {
    char *word;
    char **sentence;
    char ***monologue;
    char ****biography;
    char *****biolibrary;
    char ******lol;

    //fill data structure
    word = malloc(4 * sizeof *word); // assume it worked
    strcpy(word, "foo");

    sentence = malloc(4 * sizeof *sentence); // assume it worked
    sentence[0] = word;
    sentence[1] = word;
    sentence[2] = word;
    sentence[3] = NULL;

    monologue = malloc(4 * sizeof *monologue); // assume it worked
    monologue[0] = sentence;
    monologue[1] = sentence;
    monologue[2] = sentence;
    monologue[3] = NULL;

    biography = malloc(4 * sizeof *biography); // assume it worked
    biography[0] = monologue;
    biography[1] = monologue;
    biography[2] = monologue;
    biography[3] = NULL;

    biolibrary = malloc(4 * sizeof *biolibrary); // assume it worked
    biolibrary[0] = biography;
    biolibrary[1] = biography;
    biolibrary[2] = biography;
    biolibrary[3] = NULL;

    lol = malloc(4 * sizeof *lol); // assume it worked
    lol[0] = biolibrary;
    lol[1] = biolibrary;
    lol[2] = biolibrary;
    lol[3] = NULL;

    printf("total words in my lol: %d\n", wordsinlol(lol));

    free(lol);
    free(biolibrary);
    free(biography);
    free(monologue);
    free(sentence);
    free(word);
}

आउटपुट:

मेरे लोल में कुल शब्द: 243

6
बस यह इंगित करना चाहता था कि ए arr[a][b][c]नहीं है ***arr। पॉइंटर्स ऑफ़ पॉइंटर्स संदर्भों के संदर्भों का उपयोग करते हैं, जबकि arr[a][b][c]पंक्ति प्रमुख क्रम में एक सामान्य सरणी के रूप में संग्रहीत किया जाता है।
एमसीसीएस

170

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

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

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

void allocate(int** p)
{
  *p = (int*)malloc(sizeof(int));
}

int main()
{
  int* p = NULL;
  allocate(&p);
  *p = 42;
  free(p);
}

14
क्या अलग होगा यदि आवंटित किया गया था void allocate(int *p)और आपने इसे कहा था allocate(p)?
ア ッ ク ア

@AlexanderSupertramp हां। कोड सेगफॉल्ट होगा। कृपया सिल्वीयू का उत्तर देखें।
अभिषेक

@ आबंटन (p) और आबंटित (& p) के बीच क्या अंतर है?
15:29 बजे user2979872

1
@ आशा - क्या हम सिर्फ पॉइंटर वापस नहीं कर सकते? यदि हमें इसे शून्य रखना चाहिए तो इस परिदृश्य का व्यावहारिक उपयोग क्या है?
शबीरमैन

91
  • मान लीजिए कि आपके पास एक संकेतक है। इसका मान एक पता है।
  • लेकिन अब आप उस पते को बदलना चाहते हैं।
  • आप ऐसा कर सकते हैं। करके pointer1 = pointer2, आप pointer1 को pointer2 का पता देते हैं।
  • परंतु! यदि आप ऐसा किसी फ़ंक्शन के भीतर करते हैं, और आप चाहते हैं कि परिणाम फ़ंक्शन के बाद बनी रहे, तो आपको कुछ अतिरिक्त काम करने की आवश्यकता है। आपको बस pointer1 को इंगित करने के लिए एक नए पॉइंटर 3 की आवश्यकता है। फ़ंक्शन को पॉइंटर 3 पास करें।

  • यहाँ एक उदाहरण है। समझने के लिए, पहले नीचे दिए गए आउटपुट को देखें।

#include <stdio.h>

int main()
{

    int c = 1;
    int d = 2;
    int e = 3;
    int * a = &c;
    int * b = &d;
    int * f = &e;
    int ** pp = &a;  // pointer to pointer 'a'

    printf("\n a's value: %x \n", a);
    printf("\n b's value: %x \n", b);
    printf("\n f's value: %x \n", f);
    printf("\n can we change a?, lets see \n");
    printf("\n a = b \n");
    a = b;
    printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
    printf("\n cant_change(a, f); \n");
    cant_change(a, f);
    printf("\n a's value is now: %x, Doh! same as 'b'...  that function tricked us. \n", a);

    printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
     printf("\n change(pp, f); \n");
    change(pp, f);
    printf("\n a's value is now: %x, YEAH! same as 'f'...  that function ROCKS!!!. \n", a);
    return 0;
}

void cant_change(int * x, int * z){
    x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);
}

void change(int ** x, int * z){
    *x = z;
    printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", *x);
}

यहाँ उत्पादन है: ( यह पहले पढ़ें )

 a's value: bf94c204

 b's value: bf94c208 

 f's value: bf94c20c 

 can we change a?, lets see 

 a = b 

 a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see... 

 cant_change(a, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c208, Doh! same as 'b'...  that function tricked us. 

 NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' 

 change(pp, f); 

 ----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see

 a's value is now: bf94c20c, YEAH! same as 'f'...  that function ROCKS!!!. 

4
यह एक महान जवाब है और वास्तव में मुझे दोहरे सूचक के उद्देश्य और उपयोगिता की कल्पना करने में मदद मिली है।
जस्टिन

1
@ जस्टिन क्या आपने इस एक के ऊपर मेरे उत्तर की जांच की? इसका क्लीनर :)
ब्रायन जोसेफ स्पिनोस

10
शानदार उत्तर, बस यह समझाने की कमी है कि <code> void cant_change (int * x, int * z) </ code> विफल रहता है क्योंकि इसके 'पैरामीटर सिर्फ नए स्थानीय स्कूप किए गए पॉइंटर्स हैं जो कि प्रारंभिक रूप से a और f पॉइंटर्स हैं (इसलिए वे नहीं हैं एक ही के रूप में एक और एफ)।
पेड्रो रीस

1
सरल? वास्तव में? ;)
अल्क

1
यह उत्तर वास्तव में सूचक को सूचक के लिए सबसे सामान्य उपयोग में से एक समझाता है, धन्यवाद!
टोनीजोसी

48

को जोड़ना आशा की , प्रतिक्रिया यदि आप उदाहरण bellow के लिए एकल सूचक का उपयोग (जैसे alloc1 ()) आप स्मृति समारोह के अंदर आवंटित करने के लिए संदर्भ खो देंगे।

void alloc2(int** p) {
   *p = (int*)malloc(sizeof(int));
   **p = 10;
}

void alloc1(int* p) {
   p = (int*)malloc(sizeof(int));
   *p = 10;
}

int main(){
   int *p = NULL;
   alloc1(p);
   //printf("%d ",*p);//undefined
   alloc2(&p);
   printf("%d ",*p);//will print 10
   free(p);
   return 0;
}

ऐसा होने का कारण यह है कि alloc1सूचक में मूल्य से पारित किया जाता है। इसलिए, जब इसे mallocअंदर कॉल के परिणाम के लिए पुन: असाइन किया जाता है alloc1, तो परिवर्तन एक अलग दायरे में कोड से संबंधित नहीं होता है।


1
यदि स्थैतिक पूर्णांक सूचक है तो क्या होता है? सेगमेंटेशन फॉल्ट हो रहा है।
kapilddit

free(p)पर्याप्त नहीं है, आप के if(p) free(*p)रूप में अच्छी तरह से करने की आवश्यकता है
Shijing Lv

@ शिंगलिंग: सं। 10 के मान *pको मूल्यांकन करता है int, इसे intमुफ्त में पास करना () `एक बुरा विचार है।
अल्क

alloc1()एक स्मृति रिसाव शुरू करने में किया गया आवंटन । नि: शुल्क पास किए जाने वाले सूचक मान फ़ंक्शन से लौटकर खो जाते हैं।
alk

नहीं (!) को सी। में मॉलॉक का परिणाम देने की आवश्यकता है
alk

23

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

कल्पना कीजिए कि आपके पास एक लिंक्ड सूची में नोड्स के लिए एक संरचना है, जो शायद है

typedef struct node
{
    struct node * next;
    ....
} node;

अब आप एक remove_ifफ़ंक्शन लागू करना चाहते हैं , जो एक हटाने की कसौटी rmको स्वीकार करता है क्योंकि एक तर्क और लिंक की गई सूची का पता लगाता है: यदि कोई प्रविष्टि मानदंड (कुछ ऐसा rm(entry)==true) को संतुष्ट करती है , तो इसका नोड सूची से हटा दिया जाएगा। अंत में, remove_ifलिंक की गई सूची का सिर (जो मूल सिर से अलग हो सकता है) लौटाता है।

आप लिख सकते हैं

for (node * prev = NULL, * curr = head; curr != NULL; )
{
    node * const next = curr->next;
    if (rm(curr))
    {
        if (prev)  // the node to be removed is not the head
            prev->next = next;
        else       // remove the head
            head = next;
        free(curr);
    }
    else
        prev = curr;
    curr = next;
}

अपने forपाश के रूप में । संदेश है, दोहरे बिंदुओं के बिना, आपको prevबिंदुओं को फिर से व्यवस्थित करने के लिए एक चर बनाए रखना होगा , और दो अलग-अलग मामलों को संभालना होगा।

लेकिन डबल पॉइंटर्स के साथ, आप वास्तव में लिख सकते हैं

// now head is a double pointer
for (node** curr = head; *curr; )
{
    node * entry = *curr;
    if (rm(entry))
    {
        *curr = entry->next;
        free(entry);
    }
    else
        curr = &entry->next;
}

आपको prevअब इसकी आवश्यकता नहीं है क्योंकि आप सीधे prev->nextइंगित कर सकते हैं कि क्या इंगित किया गया है

चीजों को स्पष्ट करने के लिए, आइए कोड का थोड़ा पालन करें। हटाने के दौरान:

  1. यदि entry == *head: यह होगा *head (==*curr) = *head->next- headअब नए शीर्षक नोड के सूचक को इंगित करता है। आप इसे सीधे बदलकर करते हैंhead नए पॉइंटर में सामग्री ।
  2. यदि entry != *head: इसी तरह, *currको prev->nextइंगित किया जाता है, और अब इंगित करता है entry->next

कोई फर्क नहीं पड़ता कि किस मामले में, आप डबल पॉइंटर्स के साथ एकीकृत तरीके से पॉइंटर्स को फिर से व्यवस्थित कर सकते हैं।


22

1. मूल अवधारणा -

जब आप निम्नानुसार घोषणा करते हैं: -

1. char * ch - (जिसे चरित्र सूचक कहा जाता है)
- ch में एकल वर्ण का पता होता है।
- (* च) चरित्र के मूल्य के प्रति उदासीनता होगी ।।

2. चार ** ch -
'ch' में वर्ण बिंदुओं के एक सरणी का पता होता है। (जैसा कि 1)
'* ch' में एकल वर्ण का पता है। (ध्यान दें कि यह घोषणा में अंतर के कारण 1 से भिन्न है)।
(** च) चरित्र के सटीक मूल्य के लिए होगा।

अधिक पॉइंटर्स जोड़ना एक डेटाटाइप के आयाम का विस्तार करता है, चरित्र से स्ट्रिंग तक, स्ट्रिंग की सरणी और इतने पर ... आप इसे 1 डी, 2 डी, 3 डी मैट्रिक्स से संबंधित कर सकते हैं।

तो, पॉइंटर का उपयोग इस बात पर निर्भर करता है कि आप इसे कैसे घोषित करते हैं।

यहाँ एक सरल कोड है ..

int main()
{
    char **p;
    p = (char **)malloc(100);
    p[0] = (char *)"Apple";      // or write *p, points to location of 'A'
    p[1] = (char *)"Banana";     // or write *(p+1), points to location of 'B'

    cout << *p << endl;          //Prints the first pointer location until it finds '\0'
    cout << **p << endl;         //Prints the exact character which is being pointed
    *p++;                        //Increments for the next string
    cout << *p;
}

2. डबल पॉइंटर्स का एक और अनुप्रयोग -
(यह संदर्भ द्वारा पास को भी कवर करेगा)

मान लीजिए कि आप किसी फंक्शन से कैरेक्टर अपडेट करना चाहते हैं। यदि आप निम्नलिखित प्रयास करते हैं: -

void func(char ch)
{
    ch = 'B';
}

int main()
{
    char ptr;
    ptr = 'A';
    printf("%c", ptr);

    func(ptr);
    printf("%c\n", ptr);
}

आउटपुट एए होगा। यह कार्य नहीं करता है, क्योंकि आपके पास फ़ंक्शन के लिए "पास बाय वैल्यू" है।

ऐसा करने का सही तरीका है -

void func( char *ptr)        //Passed by Reference
{
    *ptr = 'B';
}

int main()
{
    char *ptr;
    ptr = (char *)malloc(sizeof(char) * 1);
    *ptr = 'A';
    printf("%c\n", *ptr);

    func(ptr);
    printf("%c\n", *ptr);
}

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

void func(char **str)
{
    strcpy(str, "Second");
}

int main()
{
    char **str;
    // printf("%d\n", sizeof(char));
    *str = (char **)malloc(sizeof(char) * 10);          //Can hold 10 character pointers
    int i = 0;
    for(i=0;i<10;i++)
    {
        str = (char *)malloc(sizeof(char) * 1);         //Each pointer can point to a memory of 1 character.
    }

    strcpy(str, "First");
    printf("%s\n", str);
    func(str);
    printf("%s\n", str);
}

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


#include <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; } लेकिन आप इसे डबल पॉइंटर का उपयोग किए बिना भी कर सकते हैं।
कुमार

" चार ** ch - 'ch' में वर्ण बिंदुओं के एक सरणी का पता होता है। " नहीं, इसमें बिंदुओं के एक सरणी के 1 तत्व का पता होता है char। एक सरणी के लिए एक पॉइंटर को char*इस तरह उदाहरण के लिए टाइप किया जाएगा: पॉइंटर को 42 पॉइंटर से सरणी तक char(*(*p)[42])परिभाषित करता pहै char
अल्क

आखिरी स्निपेट पूरी तरह से टूट गया है। शुरुआत के लिए: यहां *str = ... strअपरिभाषित व्यवहार को गैर-संगठित रूप से लागू किया गया है।
अल्क

यह malloc(sizeof(char) * 10);10 पॉइंटर के लिए कमरे को आवंटित नहीं करता है charलेकिन charकेवल 10 के लिए ..
alk

यह लूप for(i=0;i<10;i++) { str = ... इंडेक्स का उपयोग करने से चूक जाता है i
alk

17

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

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

#include <stdlib.h>

typedef unsigned char** handle_type;

//some data_structure that the library functions would work with
typedef struct 
{
    int data_a;
    int data_b;
    int data_c;
} LIB_OBJECT;

handle_type lib_create_handle()
{
    //initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
    handle_type handle = malloc(sizeof(handle_type));
    *handle = malloc(sizeof(LIB_OBJECT) * 10);

    return handle;
}

void lib_func_a(handle_type handle) { /*does something with array of LIB_OBJECTs*/ }

void lib_func_b(handle_type handle)
{
    //does something that takes input LIB_OBJECTs and makes more of them, so has to
    //reallocate memory for the new objects that will be created

    //first re-allocate the memory somewhere else with more slots, but don't destroy the
    //currently allocated slots
    *handle = realloc(*handle, sizeof(LIB_OBJECT) * 20);

    //...do some operation on the new memory and return
}

void lib_func_c(handle_type handle) { /*does something else to array of LIB_OBJECTs*/ }

void lib_free_handle(handle_type handle) 
{
    free(*handle);
    free(handle); 
}


int main()
{
    //create a "handle" to some memory that the library functions can use
    handle_type my_handle = lib_create_handle();

    //do something with that memory
    lib_func_a(my_handle);

    //do something else with the handle that will make it point somewhere else
    //but that's invisible to us from the standpoint of the calling the function and
    //working with the handle
    lib_func_b(my_handle); 

    //do something with new memory chunk, but you don't have to think about the fact
    //that the memory has moved under the hood ... it's still pointed to by the "handle"
    lib_func_c(my_handle);

    //deallocate the handle
    lib_free_handle(my_handle);

    return 0;
}

उम्मीद है की यह मदद करेगा,

जेसन


संभाल प्रकार अहस्ताक्षरित चार ** होने का क्या कारण है? शून्य के रूप में अच्छी तरह से काम करेगा?
कॉनर क्लार्क

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

7

सरल उदाहरण जो आपने पहले शायद कई बार देखा होगा

int main(int argc, char **argv)

दूसरे पैरामीटर में आपके पास यह है: पॉइंटर से पॉइंटर करने के लिए पॉइंटर।

ध्यान दें कि सूचक संकेतन ( char* c) और सरणी संकेतन ( char c[]) फ़ंक्शन तर्कों में विनिमेय हैं। तो आप भी लिख सकते हैं char *argv[]। दूसरे शब्दों में char *argv[]औरchar **argv विनिमेय हैं।

ऊपर जो प्रतिनिधित्व करता है वह वास्तव में चरित्र अनुक्रमों का एक क्रम है (कमांड लाइन तर्क जो स्टार्टअप पर एक कार्यक्रम के लिए दिए गए हैं)।

उपरोक्त फ़ंक्शन हस्ताक्षर के बारे में अधिक विवरण के लिए यह उत्तर भी देखें ।


2
"सूचक संकेतन ( char* c) और सरणी संकेतन ( char c[]) विनिमेय हैं" (और इसका सटीक अर्थ है) फ़ंक्शन के तर्क में । वे हालांकि बाहर के तर्कों से भिन्न हैं।
पीएमजी

6

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


5

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

void safeFree(void** memory) {
    if (*memory) {
        free(*memory);
        *memory = NULL;
    }
}

जब आप इस फ़ंक्शन को कॉल करते हैं तो आप इसे एक पॉइंटर के पते के साथ कहेंगे

void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);

अब myMemoryNULL के लिए सेट किया गया है और इसे पुनः उपयोग करने का कोई भी प्रयास बहुत स्पष्ट रूप से गलत होगा।


1
यह होना चाहिए if(*memory)औरfree(*memory);
आशा

1
अच्छा बिंदु, मस्तिष्क और कीबोर्ड के बीच संकेत हानि। मैंने इसे और अधिक समझ बनाने के लिए संपादित किया है।
जेफ फोस्टर

हम निम्नलिखित क्यों नहीं कर सकते ... शून्य सुरक्षित (शून्य * मेमोरी) {यदि (मेमोरी) {मुक्त (मेमोरी); स्मृति = पूर्ण; }}
पीटर_पेक

@Peter_pk स्मृति को शून्य करने के लिए असाइन करने से मदद नहीं मिलेगी क्योंकि आप एक पॉइंटर को मान से पास कर चुके हैं, संदर्भ द्वारा नहीं (इसलिए पॉइंटर के लिए पॉइंटर का उदाहरण)।
जेफ फोस्टर

2

उदाहरण के लिए, यदि आप गैर-संपर्क डेटा तक यादृच्छिक पहुँच चाहते हैं।

p -> [p0, p1, p2, ...]  
p0 -> data1
p1 -> data2

- सी में

T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));

आप एक पॉइंटर को स्टोर करें p जो पॉइंटर्स ऑफ़ व्यू पॉइंट की ओर इशारा करता है। प्रत्येक सूचक डेटा के एक टुकड़े को इंगित करता है।

यदि sizeof(T)बड़ा है तो sizeof(T) * nबाइट्स का एक सन्निहित ब्लॉक (यानी मॉलोक का उपयोग करना) आवंटित करना संभव नहीं हो सकता है ।


1
नहीं (!) को सी। में मॉलॉक का परिणाम देने की आवश्यकता है
alk

2

एक चीज जो मैं उन्हें लगातार उपयोग करता हूं वह यह है कि जब मेरे पास ऑब्जेक्ट्स की एक सरणी होती है और मुझे विभिन्न क्षेत्रों द्वारा उन पर लुकअप (बाइनरी सर्च) करने की आवश्यकता होती है।
मैं मूल सरणी रखता हूँ ...

int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);

फिर ऑब्जेक्ट्स पर सॉर्ट किए गए पॉइंटर्स की एक सरणी बनाएं।

int compare_object_by_name( const void *v1, const void *v2 ) {
  OBJECT *o1 = *(OBJECT **)v1;
  OBJECT *o2 = *(OBJECT **)v2;
  return (strcmp(o1->name, o2->name);
}

OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
  int i = 0;
  for( ; i<num_objects; i++)
    object_ptrs_by_name[i] = original_array+i;
  qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);

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


2

डबल पॉइंटर्स क्यों?

उद्देश्य एक फ़ंक्शन का उपयोग करके छात्र को इंगित करने के लिए बदलना है।

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


typedef struct Person{
    char * name;
} Person; 

/**
 * we need a ponter to a pointer, example: &studentA
 */
void change(Person ** x, Person * y){
    *x = y; // since x is a pointer to a pointer, we access its value: a pointer to a Person struct.
}

void dontChange(Person * x, Person * y){
    x = y;
}

int main()
{

    Person * studentA = (Person *)malloc(sizeof(Person));
    studentA->name = "brian";

    Person * studentB = (Person *)malloc(sizeof(Person));
    studentB->name = "erich";

    /**
     * we could have done the job as simple as this!
     * but we need more work if we want to use a function to do the job!
     */
    // studentA = studentB;

    printf("1. studentA = %s (not changed)\n", studentA->name);

    dontChange(studentA, studentB);
    printf("2. studentA = %s (not changed)\n", studentA->name);

    change(&studentA, studentB);
    printf("3. studentA = %s (changed!)\n", studentA->name);

    return 0;
}

/**
 * OUTPUT:
 * 1. studentA = brian (not changed)
 * 2. studentA = brian (not changed)
 * 3. studentA = erich (changed!)
 */

1
नहीं (!) को सी। में मॉलॉक का परिणाम देने की आवश्यकता है
alk

2

निम्नलिखित एक बहुत ही सरल C ++ उदाहरण है जो दिखाता है कि यदि आप किसी ऑब्जेक्ट को इंगित करने के लिए पॉइंटर सेट करने के लिए फ़ंक्शन का उपयोग करना चाहते हैं, तो आपको पॉइंटर को पॉइंटर की आवश्यकता है । अन्यथा, सूचक अशक्त करने के लिए reverting रखेंगे

(C C ++ उत्तर, लेकिन मेरा मानना ​​है कि यह C में समान है)

(इसके अलावा, संदर्भ के लिए: Google ("मान c ++ द्वारा पास") = "डिफ़ॉल्ट रूप से, C ++ में तर्क मूल्य द्वारा पारित किए जाते हैं। जब कोई तर्क मूल्य द्वारा पारित किया जाता है, तो तर्क का मान फ़ंक्शन के पैरामीटर में कॉपी किया जाता है।")

इसलिए हम सूचक bको स्ट्रिंग के बराबर सेट करना चाहते हैं a

#include <iostream>
#include <string>

void Function_1(std::string* a, std::string* b) {
  b = a;
  std::cout << (b == nullptr);  // False
}

void Function_2(std::string* a, std::string** b) {
  *b = a;
  std::cout << (b == nullptr);  // False
}

int main() {
  std::string a("Hello!");
  std::string* b(nullptr);
  std::cout << (b == nullptr);  // True

  Function_1(&a, b);
  std::cout << (b == nullptr);  // True

  Function_2(&a, &b);
  std::cout << (b == nullptr);  // False
}

// Output: 10100

रेखा पर क्या होता है Function_1(&a, b);?

  • "मान" &main::a(एक पता) को पैरामीटर में कॉपी किया जाता है std::string* Function_1::a। इसलिए Function_1::aस्ट्रिंग के लिए एक सूचक (यानी मेमोरी एड्रेस) है main::a

  • main::b(स्मृति में एक पता) के "मूल्य" को पैरामीटर में कॉपी किया जाता है std::string* Function_1::b। इसलिए स्मृति में अब इनमें से 2 पते हैं, दोनों अशक्त बिंदु। रेखा पर b = a;, स्थानीय चर Function_1::bको बराबर Function_1::a(= &main::a) में बदल दिया जाता है , लेकिन चर main::bअपरिवर्तित होता है। कॉल करने के बाद Function_1, main::bअभी भी एक अशक्त सूचक है।

रेखा पर क्या होता है Function_2(&a, &b);?

  • aचर का उपचार समान है: फ़ंक्शन के भीतर, Function_2::aस्ट्रिंग का पता है main::a

  • लेकिन चर bअब एक पॉइंटर को पॉइंटर के रूप में पारित किया जा रहा है। &main::b( पॉइंटर का पताmain::b ) के "मूल्य" को कॉपी किया जाता है std::string** Function_2::b। इसलिए Function_2 के भीतर, इस के रूप में dereferencing *Function_2::bतक पहुंच और संशोधित होगी main::b। तो लाइन *b = a;वास्तव में (= main::bएक पते) के बराबर सेटिंग Function_2::a(= पता main::a) है जो हम चाहते हैं।

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

(एक अपवाद है अगर पैरामीटर एक संदर्भ है, जैसे कि std::string& a। लेकिन आमतौर पर ये होते हैं const। आम तौर पर, यदि आप कॉल करते हैं f(x), तो xएक वस्तु है जिसे आपको यह मान लेना चाहिए कि संशोधित f नहीं होगाx । लेकिन अगर xकोई सूचक है, तो आप। मान लें कि द्वारा इंगित की गई वस्तु f को संशोधित किया जा सकता है x।)


C C का उत्तर देने के लिए C ++ कोड सबसे अच्छा विचार नहीं है।
अल्क

1

पार्टी में थोड़ी देर हो गई, लेकिन उम्मीद है कि इससे किसी को मदद मिलेगी।

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

/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows float-pointers
    double** A = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(A == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols floats
    for(int i = 0; i < num_rows; i++){
        A[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(A[i] == NULL){
            for(int j = 0; j < i; j++){
                free(A[j]);
            }
            free(A);
            return NULL;
        }
    }
    return A;
} 

यहाँ एक उदाहरण है:

double**       double*           double
             -------------       ---------------------------------------------------------
   A ------> |   A[0]    | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
             | --------- |       ---------------------------------------------------------
             |   A[1]    | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             |   A[i]    | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
             | --------- |       ---------------------------------------------------------
             |     .     |                                    .
             |     .     |                                    .
             |     .     |                                    .
             | --------- |       ---------------------------------------------------------
             | A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
             -------------       ---------------------------------------------------------

डबल-पॉइंटर-टू-डबल-पॉइंटर A मेमोरी ब्लॉक के पहले तत्व A [0] को इंगित करता है जिसके तत्व डबल-पॉइंटर्स ही होते हैं। आप मैट्रिक्स की पंक्तियों के रूप में इन डबल-पॉइंटर्स की कल्पना कर सकते हैं। यही कारण है कि हर डबल-पॉइंटर टाइप डबल के num_cols तत्वों के लिए मेमोरी आवंटित करता है। इसके अलावा A [i] i-th पंक्ति की ओर इशारा करता है, यानी A [i] A [i] [0] की ओर इशारा करता है और यह i-th पंक्ति के लिए मेमोरी ब्लॉक का पहला दोहरा तत्व है। अंत में, आप तत्व को ए [i] [जे] के साथ आसानी से i-th रो और जे-थ कॉलम में एक्सेस कर सकते हैं।

यहाँ एक पूर्ण उदाहरण है जो उपयोग को प्रदर्शित करता है:

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

/* Initializes a matrix */
double** init_matrix(int num_rows, int num_cols){
    // Allocate memory for num_rows double-pointers
    double** matrix = calloc(num_rows, sizeof(double*));
    // return NULL if the memory couldn't allocated
    if(matrix == NULL) return NULL;
    // For each double-pointer (row) allocate memory for num_cols
    // doubles
    for(int i = 0; i < num_rows; i++){
        matrix[i] = calloc(num_cols, sizeof(double));
        // return NULL if the memory couldn't allocated
        // and free the already allocated memory
        if(matrix[i] == NULL){
            for(int j = 0; j < i; j++){
                free(matrix[j]);
            }
            free(matrix);
            return NULL;
        }
    }
    return matrix;
}

/* Fills the matrix with random double-numbers between -1 and 1 */
void randn_fill_matrix(double** matrix, int rows, int cols){
    for (int i = 0; i < rows; ++i){
        for (int j = 0; j < cols; ++j){
            matrix[i][j] = (double) rand()/RAND_MAX*2.0-1.0;
        }
    }
}


/* Frees the memory allocated by the matrix */
void free_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        free(matrix[i]);
    }
    free(matrix);
}

/* Outputs the matrix to the console */
void print_matrix(double** matrix, int rows, int cols){
    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            printf(" %- f ", matrix[i][j]);
        }
        printf("\n");
    }
}


int main(){
    srand(time(NULL));
    int m = 3, n = 3;
    double** A = init_matrix(m, n);
    randn_fill_matrix(A, m, n);
    print_matrix(A, m, n);
    free_matrix(A, m, n);
    return 0;
}

0

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

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


0

पॉइंटर बनाम वैरिएबल के संशोधित मूल्य की तुलना करें :

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

void changeA(int (*a))
{
  (*a) = 10;
}

void changeP(int *(*P))
{
  (*P) = malloc(sizeof((*P)));
}

int main(void)
{
  int A = 0;

  printf("orig. A = %d\n", A);
  changeA(&A);
  printf("modi. A = %d\n", A);

  /*************************/

  int *P = NULL;

  printf("orig. P = %p\n", P);
  changeP(&P);
  printf("modi. P = %p\n", P);

  free(P);

  return EXIT_SUCCESS;
}

इससे मुझे पॉइंटर के रिटर्निंग वैल्यू से बचने में मदद मिली जब पॉइंटर को फंक्शन (जिसे लिंक्ड लिस्ट में इस्तेमाल किया गया था) द्वारा संशोधित किया गया था।

OLD (खराब):

int *func(int *P)
{
  ...
  return P;
}

int main(void)
{
  int *pointer;
  pointer = func(pointer);
  ...
}    

नया (बेहतर):

void func(int **pointer)
{
  ...
}

int main(void)
{
  int *pointer;
  func(&pointer);
  ...
}    
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.