সি-তে কখন ডাবল ইন্ডিয়ারেশন ব্যবহার করা উচিত? কেউ উদাহরণ দিয়ে ব্যাখ্যা করতে পারেন?
আমি যা জানি তা হ'ল ডাবল ইন্ডিরেশন একটি পয়েন্টারের কাছে পয়েন্টার। আমার কেন একটি পয়েন্টারের কাছে পয়েন্টার লাগবে?
সি-তে কখন ডাবল ইন্ডিয়ারেশন ব্যবহার করা উচিত? কেউ উদাহরণ দিয়ে ব্যাখ্যা করতে পারেন?
আমি যা জানি তা হ'ল ডাবল ইন্ডিরেশন একটি পয়েন্টারের কাছে পয়েন্টার। আমার কেন একটি পয়েন্টারের কাছে পয়েন্টার লাগবে?
উত্তর:
আপনি যদি অক্ষরের একটি তালিকা (একটি শব্দ) রাখতে চান তবে আপনি ব্যবহার করতে পারেন 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
arr[a][b][c]
নয় ***arr
। পয়েন্টারগুলির পয়েন্টার রেফারেন্সের রেফারেন্স ব্যবহার করে, যখন arr[a][b][c]
সারি বড় ক্রমে সাধারণ অ্যারে হিসাবে সংরক্ষণ করা হয়।
একটি কারণ হ'ল আপনি কোনও ফাংশনটিতে আউট পয়েন্টারের মানটি ফাংশন আর্গুমেন্ট হিসাবে পরিবর্তন করতে চান, এটি করার জন্য আপনাকে পয়েন্টারের কাছে পয়েন্টার প্রয়োজন।
সহজ কথায়, ব্যবহার করুন **
আপনি এমনকি একটি ফাংশন কল বাইরে সংরক্ষণ (বা পরিবর্তন বজায় রাখা) স্মৃতি-বরাদ্দ বা অ্যাসাইনমেন্ট করতে চান না। (সুতরাং, ডাবল পয়েন্টার আর্গ দিয়ে এই জাতীয় ফাংশনটি পাস করুন))
এটি খুব ভাল উদাহরণ নাও হতে পারে তবে এটি আপনাকে প্রাথমিক ব্যবহারটি দেখাবে:
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
এবং আপনি এটি হিসাবে ডাকেন তবে কী আলাদা হবে allocate(p)
?
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!!!.
যোগ করা হচ্ছে আশা এর , প্রতিক্রিয়া যদি আপনি উদাহরণ নিচে থেকে একক পয়েন্টার ব্যবহার (যেমন 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
, তখন পরিবর্তনটি অন্য কোনও ক্ষেত্রে কোডের সাথে সম্পর্কিত নয়।
free(p)
পর্যাপ্ত নয়, আপনার if(p) free(*p)
পাশাপাশিও দরকার
*p
মূল্যায়ন int
করে এটি int
বিনামূল্যে () passing এ পাস করা একটি খারাপ ধারণা।
alloc1()
মেমরি ফুটোয়ের পরিচয় দেয়। বিনা মূল্যে পাস করার পয়েন্টার মানটি ফাংশন থেকে ফিরে এসে হারিয়ে যায়।
আমি নীচে সংক্ষেপে এই ব্লগ পোস্ট থেকে আজ একটি খুব ভাল উদাহরণ দেখেছি ।
কল্পনা করুন যে কোনও লিঙ্কযুক্ত তালিকায় নোডের জন্য আপনার কাঠামো রয়েছে যা সম্ভবত 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
নির্দেশ করেছেন তা সরাসরি সংশোধন করতে পারবেন ।
জিনিসগুলি আরও পরিষ্কার করার জন্য কোডটি কিছুটা অনুসরণ করা যাক। অপসারণের সময়:
entry == *head
: এটি হবে *head (==*curr) = *head->next
- head
এখন নতুন শিরোনাম নোডের পয়েন্টারে নির্দেশ করুন। আপনি সরাসরি head
এর সামগ্রীতে একটি নতুন পয়েন্টারে পরিবর্তন করে এটি করেন।entry != *head
: অনুরূপভাবে, *curr
কি prev->next
নির্দেশিত এবং এখন পয়েন্ট entry->next
।যাই হোক না কেন, আপনি ডাবল পয়েন্টার সহ একীভূত পয়েন্টারগুলিকে পুনরায় সংগঠিত করতে পারেন।
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])
সংজ্ঞায়িত । p
char
*str = ...
str
অনিচ্ছাকৃত চাওয়া অনির্ধারিত আচরণকে অবজ্ঞা করা হয়।
malloc(sizeof(char) * 10);
10 পয়েন্টারের জন্য char
কেবল 10 টির জন্য ঘর বরাদ্দ করে না char
..
for(i=0;i<10;i++) { str = ...
সূচকটি ব্যবহার করতে মিস করে i
।
পয়েন্টারগুলিতে পয়েন্টারগুলি মেমোরিতে "হ্যান্ডলগুলি" হিসাবে কার্যকর হয় যেখানে আপনি পুনরায় লোকেশনযোগ্য স্মৃতিতে ফাংশনগুলির মধ্যে একটি "হ্যান্ডেল" এর কাছাকাছি যেতে চান। মূলত এর অর্থ হ'ল ফাংশনটি হ্যান্ডেল ভেরিয়েবলের ভিতরে পয়েন্টারটির দ্বারা চিহ্নিত মেমরিটিকে পরিবর্তন করতে পারে এবং হ্যান্ডেলটি ব্যবহার করা প্রতিটি ফাংশন বা অবজেক্ট সঠিকভাবে সদ্য স্থানান্তরিত (বা বরাদ্দ) মেমরিটির দিকে নির্দেশ করবে। লাইব্রেরিগুলি "অস্বচ্ছ" ডেটা-টাইপের সাথে এটি করতে পছন্দ করে, এটি ডেটা-টাইপগুলি ছিল যে স্মৃতিটি নির্দেশিত হওয়ার সাথে সাথে তারা কী করছে তা নিয়ে আপনাকে চিন্তা করতে হবে না, আপনি কেবল "হ্যান্ডেল" এর মধ্য দিয়ে পাস করবেন গ্রন্থাগারের ফাংশনগুলি সেই স্মৃতিতে কিছু ক্রিয়াকলাপ সম্পাদন করে ...
এই ক্ষেত্রে:
#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;
}
আশাকরি এটা সাহায্য করবে,
জেসন
unsigned char
বিশেষত ব্যবহৃত হয় কারণ আমরা বাইনারি ডেটাতে একটি পয়েন্টার সংরক্ষণ করছি যা কাঁচা বাইট হিসাবে উপস্থাপিত হবে। ব্যবহারের void
জন্য কোনও পর্যায়ে একটি কাস্ট প্রয়োজন হবে এবং সাধারণত যা করা হচ্ছে তার উদ্দেশ্য হিসাবে তত পাঠযোগ্য নয়।
int main(int argc, char **argv)
দ্বিতীয় প্যারামিটারে আপনার এটি রয়েছে: পয়েন্টার থেকে চরকে পয়েন্টার।
মনে রাখবেন যে পয়েন্টার স্বরলিপি ( char* c
) এবং অ্যারের স্বরলিপি ( char c[]
) ফাংশন আর্গুমেন্টে বিনিময়যোগ্য। সুতরাং আপনি লিখতে পারে char *argv[]
। অন্য কথায় char *argv[]
এবং char **argv
বিনিময়যোগ্য।
উপরেরটি যা উপস্থাপন করে তা আসলে চরিত্রের ক্রমগুলির একটি অ্যারে (শুরুর সময় কোনও প্রোগ্রামকে দেওয়া কমান্ড লাইন আর্গুমেন্ট)।
উপরের ফাংশন স্বাক্ষর সম্পর্কে আরও তথ্যের জন্য এই উত্তরটিও দেখুন ।
char* c
) এবং অ্যারের স্বরলিপি ( char c[]
) বিনিময়যোগ্য" (এবং একই সঠিক অর্থ রয়েছে) ফাংশন আর্গুমেন্টে । এগুলি ফাংশন আর্গুমেন্টের বাইরে অবশ্য আলাদা।
উদাহরণস্বরূপ, আপনি এটি নিশ্চিত করতে চাইবেন যে আপনি যখন কোনও কিছুর মেমরি মুক্ত করবেন তখন আপনি পয়েন্টারটি পরে নਾਲকে সেট করবেন।
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
আপনি যখন এই ফাংশনটি কল করবেন তখন আপনি এটি কোনও পয়েন্টারের ঠিকানায় কল করবেন
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
এখন myMemory
NULL তে সেট করা আছে এবং এটির পুনরায় ব্যবহারের যে কোনও প্রচেষ্টা খুব স্পষ্টতই ভুল হবে।
if(*memory)
এবংfree(*memory);
উদাহরণস্বরূপ, আপনি যদি অবিসংবাদিত ডেটা এলোমেলো অ্যাক্সেস চান।
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
বাইটগুলির একটি সংলগ্ন ব্লক (অর্থাত্ মলোক ব্যবহার করে) বরাদ্দ করা সম্ভব নাও হতে পারে ।
আমি নিয়মিত তাদের জন্য যে জিনিসটি ব্যবহার করি তা হ'ল আমার যখন অবজেক্টগুলির একটি অ্যারে থাকে এবং আমার বিভিন্ন ক্ষেত্রগুলি দ্বারা তাদের উপর লুকআপ (বাইনারি অনুসন্ধান) করা উচিত।
আমি মূল অ্যারে রাখি ...
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);
আপনার প্রয়োজন অনুসারে আপনি যতগুলি সাজানো পয়েন্টার অ্যারে তৈরি করতে পারেন, তারপরে আপনার থাকা ডেটা দ্বারা আপনার প্রয়োজনীয় অবজেক্টটি অ্যাক্সেস করতে বাছাই করা পয়েন্টার অ্যারেতে বাইনারি অনুসন্ধান ব্যবহার করুন। অবজেক্টের মূল অ্যারেটি অরক্ষিত থাকতে পারে তবে প্রতিটি পয়েন্টার অ্যারে তাদের নির্দিষ্ট ক্ষেত্র অনুসারে বাছাই করা হবে।
কেন ডাবল পয়েন্টার?
উদ্দেশ্যটি হ'ল স্টুডেন্টএ কোন ফাংশনটি ব্যবহার করে তা নির্দেশ করে।
#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!)
*/
নীচে একটি খুব সাধারণ সি ++ উদাহরণ রয়েছে যা দেখায় যে আপনি কোনও পয়েন্টারটিতে পয়েন্টার সেট করার জন্য যদি কোনও ফাংশন ব্যবহার করতে চান তবে আপনার পয়েন্টারটিতে একটি পয়েন্টার প্রয়োজন । অন্যথায়, পয়েন্টারটি নালায় ফিরতে থাকবে ।
(একটি সি ++ উত্তর, তবে আমি বিশ্বাস করি এটি সি তে একই)
(এছাড়াও, রেফারেন্সের জন্য: গুগল ("মান দ্বারা সি ++" পাস করুন)) "ডিফল্টরূপে সি ++ এর মধ্যে আর্গুমেন্টগুলি মান দ্বারা পাস হয় 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
))
পার্টিতে কিছুটা দেরি হলেও আশা করি এটি কারও সহায়ক হবে।
সি অ্যারেগুলিতে সর্বদা স্ট্যাকের উপর মেমরি বরাদ্দ করা হয়, ফলস্বরূপ বর্তমান ব্লকের শেষের দিকে পৌঁছে গেলে স্ট্যাকের বরাদ্দ হওয়া মেমরি স্বয়ংক্রিয়ভাবে মুক্তি পায় এই কারণে কোনও ফাংশন একটি (অ-স্থিতিশীল) অ্যারে ফিরিয়ে দিতে পারে না। যখন আপনি দ্বি-মাত্রিক অ্যারেগুলি (যেমন ম্যাট্রিকেস) নিয়ে ডিল করতে চান এবং কয়েকটি ফাংশন প্রয়োগ করতে পারেন যা ম্যাট্রিকগুলি পরিবর্তন করতে এবং ফিরে আসতে পারে তা সত্যিই বিরক্তিকর। এটি অর্জন করতে, আপনি গতিশীল বরাদ্দ মেমরির সাথে ম্যাট্রিক্স প্রয়োগ করতে পয়েন্টার-টু-পয়েন্টার ব্যবহার করতে পারেন:
/* 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;
}
আমি কাজের জন্য কিছু প্রোগ্রামিং করার সময় আমি আজ ডাবল পয়েন্টার ব্যবহার করেছি, তাই আমি কেন সেগুলি ব্যবহার করতে হয়েছিল তা উত্তর দিতে পারি (এটি প্রথমবারের মতো আমাকে ডাবল পয়েন্টার ব্যবহার করতে হয়েছিল)। আমাদের কিছু কাঠামোর সদস্য যারা বাফারগুলিতে থাকা ফ্রেমের রিয়েল টাইম এনকোডিংয়ের সাথে ডিল করতে হয়েছিল। এনকোডারটিতে আমাদের সেই স্ট্রাকচারগুলির মধ্যে একটিতে একটি পয়েন্টার ব্যবহার করতে হয়েছিল। সমস্যাটি হ'ল আমাদের পয়েন্টারটি অন্য থ্রেড থেকে অন্য স্ট্রাকচারের দিকে নির্দেশ করার জন্য পরিবর্তন করা হচ্ছে। এনকোডারে বর্তমান কাঠামোটি ব্যবহার করার জন্য, আমাকে অন্য থ্রেডে পরিবর্তিত হওয়া পয়েন্টারটি নির্দেশ করতে ডাবল পয়েন্টার ব্যবহার করতে হয়েছিল। কমপক্ষে আমাদের পক্ষে এটি প্রথমে সুস্পষ্ট ছিল না যে আমাদের এই পদ্ধতিটি গ্রহণ করতে হবে। প্রক্রিয়াটিতে প্রচুর ঠিকানা ছাপা হয়েছিল :))।
আপনি যখন আপনার অ্যাপ্লিকেশনের অন্যান্য জায়গায় পরিবর্তিত পয়েন্টারগুলিতে কাজ করেন তখন আপনি ডাবল পয়েন্টার ব্যবহার করতে হবে। আপনি যখন হার্ডওয়ারটি আপনাকে প্রত্যাবর্তন করবে এবং আপনাকে ঠিকানা দেবে তখন আপনি ডাবল পয়েন্টারগুলি আবশ্যক হতে পারে।
পরিবর্তনকের বনাম পয়েন্টারের পরিবর্তিত মানের তুলনা করুন :
#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);
...
}
double*
।