ডাবল ইন্ডিরিশন কেন ব্যবহার করবেন? বা কেন পয়েন্টারগুলিতে পয়েন্টার ব্যবহার করবেন?


272

সি-তে কখন ডাবল ইন্ডিয়ারেশন ব্যবহার করা উচিত? কেউ উদাহরণ দিয়ে ব্যাখ্যা করতে পারেন?

আমি যা জানি তা হ'ল ডাবল ইন্ডিরেশন একটি পয়েন্টারের কাছে পয়েন্টার। আমার কেন একটি পয়েন্টারের কাছে পয়েন্টার লাগবে?


49
সতর্ক হোন; "ডাবল পয়েন্টার" শব্দটিও টাইপটিকে বোঝায় double*
কিথ থম্পসন

একটি নোট নিন: সি এবং সি ++ এর জন্য এই প্রশ্নের উত্তর আলাদা - এই পুরানো প্রশ্নটিতে সি + ট্যাগ যুক্ত করবেন না।
BЈовић

উত্তর:


479

আপনি যদি অক্ষরের একটি তালিকা (একটি শব্দ) রাখতে চান তবে আপনি ব্যবহার করতে পারেন char *word

আপনি যদি শব্দগুলির একটি তালিকা চান (একটি বাক্য), আপনি ব্যবহার করতে পারেন char **sentence

আপনি যদি বাক্যগুলির একটি তালিকা (একাডেমিক) চান, আপনি ব্যবহার করতে পারেন char ***monologue

আপনি যদি মনোলোগের একটি তালিকা (একটি জীবনী) চান তবে আপনি এটি ব্যবহার করতে পারেন char ****biography

আপনি যদি জীবনীগুলির একটি তালিকা চান (একটি বায়ো-লাইব্রেরি), আপনি ব্যবহার করতে পারেন char *****biolibrary

আপনি যদি বায়ো-লাইব্রেরির একটি তালিকা চান (a ?? lol), আপনি ব্যবহার করতে পারেন 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

একটি কারণ হ'ল আপনি কোনও ফাংশনটিতে আউট পয়েন্টারের মানটি ফাংশন আর্গুমেন্ট হিসাবে পরিবর্তন করতে চান, এটি করার জন্য আপনাকে পয়েন্টারের কাছে পয়েন্টার প্রয়োজন।

সহজ কথায়, ব্যবহার করুন **আপনি এমনকি একটি ফাংশন কল বাইরে সংরক্ষণ (বা পরিবর্তন বজায় রাখা) স্মৃতি-বরাদ্দ বা অ্যাসাইনমেন্ট করতে চান না। (সুতরাং, ডাবল পয়েন্টার আর্গ দিয়ে এই জাতীয় ফাংশনটি পাস করুন))

এটি খুব ভাল উদাহরণ নাও হতে পারে তবে এটি আপনাকে প্রাথমিক ব্যবহারটি দেখাবে:

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)?
レ ッ ク ス

পছন্দ করুন কোডটি সেগফল্ট করবে। সিলভিউ এর উত্তর দেখুন।
অভিষেক

@ আশা বরাদ্দ (পি) এবং বরাদ্দ (& পি) মধ্যে পার্থক্য কি?
ব্যবহারকারী 2979872

1
@ আশা - আমরা কি কেবল পয়েন্টারটি ফিরিয়ে দিতে পারি না? আমাদের যদি অবশ্যই তা বাতিল রাখতে হয় তবে এই দৃশ্যের ব্যবহারিক ব্যবহার কী?
শাবিরমীন

91
  • ধরা যাক আপনার একটি পয়েন্টার আছে। এর মান একটি ঠিকানা।
  • তবে এখন আপনি এই ঠিকানাটি পরিবর্তন করতে চান।
  • আপনি পারে। করে pointer1 = pointer2, আপনি পয়েন্টার 1 কে পয়েন্টার 2 এর ঠিকানা দিন।
  • কিন্ত! যদি আপনি এটি কোনও ফাংশনের মধ্যে করেন এবং আপনি ফাংশনটি শেষ হওয়ার পরে ফলাফলটি অবিরত রাখতে চান, আপনার কিছু অতিরিক্ত কাজ করা দরকার। আপনাকে কেবলমাত্র পয়েন্টার 1 এ নির্দেশ করতে একটি নতুন পয়েন্টার 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> শূন্য ক্যান্ট_চেঞ্জ (ইন্ট * এক্স, ইনট * জেড) ব্যাখ্যা করার অভাব রয়েছে </ কোড> ব্যর্থ হয়েছে কারণ এর 'প্যারামিটারগুলি কেবলমাত্র নতুন স্থানীয় স্কোপড পয়েন্টার যা একইভাবে a এবং f পয়েন্টারগুলির সূচনা হয় (তাই তারা নয় একটি এবং চ হিসাবে একই)।
পেড্রো রেইস

1
সরল? সত্যি? ;)
ALK

1
এই উত্তরটি সত্যিই পয়েন্টারগুলির কাছে পয়েন্টারগুলির একটি সাধারণ ব্যবহার ব্যাখ্যা করে, ধন্যবাদ!
টনিজোসি

48

যোগ করা হচ্ছে আশা এর , প্রতিক্রিয়া যদি আপনি উদাহরণ নিচে থেকে একক পয়েন্টার ব্যবহার (যেমন 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
P স্থিতিশীল পূর্ণসংখ্যার পয়েন্টার হলে কী হবে? সেগমেন্টেশন ত্রুটি প্রাপ্তি।
kapilddit

free(p)পর্যাপ্ত নয়, আপনার if(p) free(*p)পাশাপাশিও দরকার
শিজিং Lv

@ শিজিংএলভি: নং 10 এর মূল্যমানকে *pমূল্যায়ন intকরে এটি intবিনামূল্যে () passing এ পাস করা একটি খারাপ ধারণা।
alk

বরাদ্দ করা alloc1()মেমরি ফুটোয়ের পরিচয় দেয়। বিনা মূল্যে পাস করার পয়েন্টার মানটি ফাংশন থেকে ফিরে এসে হারিয়ে যায়।
alk

কোনও (!)
ম্যালোকের

23

আমি নীচে সংক্ষেপে এই ব্লগ পোস্ট থেকে আজ একটি খুব ভাল উদাহরণ দেখেছি ।

কল্পনা করুন যে কোনও লিঙ্কযুক্ত তালিকায় নোডের জন্য আপনার কাঠামো রয়েছে যা সম্ভবত probably

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. if entry != *head: অনুরূপভাবে, *currকি prev->nextনির্দেশিত এবং এখন পয়েন্ট entry->next

যাই হোক না কেন, আপনি ডাবল পয়েন্টার সহ একীভূত পয়েন্টারগুলিকে পুনরায় সংগঠিত করতে পারেন।


22

1. বেসিক ধারণা -

আপনি যখন নিম্নলিখিত হিসাবে ঘোষণা করবেন: -

1. চর * সি - (চরিত্রের পয়েন্টার বলা হয়)
- সি-তে একটি একক অক্ষরের ঠিকানা থাকে।
- (* সিএইচ) চরিত্রের মানকে অবলম্বন করবে ..

২. চর ** সিএইচ -
'সিএইচ' এর মধ্যে অক্ষরের পয়েন্টারগুলির একটি অ্যারের ঠিকানা রয়েছে। (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;
}

২. ডাবল পয়েন্টারগুলির অন্য একটি অ্যাপ্লিকেশন -
(এটি রেফারেন্স সহ পাসও কাভার করবে)

মনে করুন আপনি কোনও ফাংশন থেকে একটি চরিত্র আপডেট করতে চান। আপনি যদি নিম্নলিখিতটি চেষ্টা করেন: -

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; } তবে আপনি ডাবল পয়েন্টারটিও ব্যবহার না করে এটি করতে পারেন।
কুমার

" চর ** সিএইচ - 'চ' তে অক্ষরের পয়েন্টারগুলির একটি অ্যারের ঠিকানা রয়েছে। " না, এতে charপয়েন্টারগুলির একটি অ্যারের 1 ম উপাদানটির ঠিকানা রয়েছে contains একটি অ্যারের একটি পয়েন্টার char*উদাহরণস্বরূপ টাইপ করা হবে: 42 পয়েন্টার একটি অ্যারে পয়েন্টার হিসাবে char(*(*p)[42])সংজ্ঞায়িত । pchar
alk

শেষ স্নিপেট সম্পূর্ণ ভেঙে গেছে। প্রারম্ভিকদের জন্য: এখানে *str = ... strঅনিচ্ছাকৃত চাওয়া অনির্ধারিত আচরণকে অবজ্ঞা করা হয়।
alk

এটি malloc(sizeof(char) * 10);10 পয়েন্টারের জন্য charকেবল 10 টির জন্য ঘর বরাদ্দ করে না char..
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
ভাল পয়েন্ট, মস্তিষ্ক এবং কীবোর্ডের মধ্যে সিগন্যাল ক্ষতি। আমি আরও সম্পাদনা করার জন্য এটি সম্পাদনা করেছি।
জেফ ফস্টার

আমরা নিম্নলিখিতগুলি কেন করতে পারি না ... অকার্যকর নিরাপদফ্রি (শূন্য * মেমরি) {যদি (স্মৃতি) {মুক্ত (স্মৃতি); স্মৃতি = NULL; ।}
পিটার_পিকে

@ পিটার_পি কে মেমোরি নালায় অর্পণ করা কোনও উপকারে আসবে না কারণ আপনি রেফারেন্সের মাধ্যমে নয় বরং মান দ্বারা একটি পয়েন্টার পাস করেছেন (সুতরাং পয়েন্টারের কাছে পয়েন্টারের উদাহরণ)।
জেফ ফস্টার 17

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
কোনও (!) সি তে
ম্যালোকের

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
কোনও (!)
ম্যালোকের

2

নীচে একটি খুব সাধারণ সি ++ উদাহরণ রয়েছে যা দেখায় যে আপনি কোনও পয়েন্টারটিতে পয়েন্টার সেট করার জন্য যদি কোনও ফাংশন ব্যবহার করতে চান তবে আপনার পয়েন্টারটিতে একটি পয়েন্টার প্রয়োজন । অন্যথায়, পয়েন্টারটি নালায় ফিরতে থাকবে

(একটি সি ++ উত্তর, তবে আমি বিশ্বাস করি এটি সি তে একই)

(এছাড়াও, রেফারেন্সের জন্য: গুগল ("মান দ্বারা সি ++" পাস করুন)) "ডিফল্টরূপে সি ++ এর মধ্যে আর্গুমেন্টগুলি মান দ্বারা পাস হয় an

সুতরাং আমরা পয়েন্টারটি 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। সুতরাং মেমরিতে এই ঠিকানাগুলির মধ্যে এখন দুটি, নাল পয়েন্টার। লাইনে 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 ))


সি প্রশ্নের উত্তর দেওয়ার জন্য সি ++ কোড করা সেরা ধারণা নয়।
alk

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 [0] এর দিকে নির্দেশ করে যার উপাদানগুলি নিজেই ডাবল পয়েন্টার। আপনি এই ডাবল পয়েন্টারগুলিকে ম্যাট্রিক্সের সারি হিসাবে কল্পনা করতে পারেন। এই কারণেই প্রতিটি ডাবল-পয়েন্টার ডাবল টাইপের num_col উপাদানগুলির জন্য মেমরি বরাদ্দ করে। তবুও এ [i] আই-তম সারির দিকে নির্দেশ করে, অর্থাৎ এ [আই] এ [পয়েন্ট] [i] [0] এ নির্দেশ করে এবং এটি আই-ম সারির জন্য মেমরি ব্লকের প্রথম ডাবল-উপাদান মাত্র। অবশেষে, আপনি আই-তম সারিতে এবং জে-থ কলামের এলিমেন্টটি সহজেই [[i] [জে] এর মাধ্যমে অ্যাক্সেস করতে পারবেন।

ব্যবহারের প্রমাণ দেয় এমন একটি সম্পূর্ণ উদাহরণ এখানে:

#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;
}

পয়েন্টারটি যখন ফাংশন (একক লিঙ্কযুক্ত তালিকায় ব্যবহৃত) দ্বারা সংশোধন করা হয়েছিল তখন পয়েন্টারের মূল্য ফেরানো এড়াতে এটি আমাকে সহায়তা করেছিল।

পুরানো (খারাপ):

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);
  ...
}    
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.