C में दोहरा अप्रत्यक्ष उपयोग कब किया जाना चाहिए? क्या कोई उदाहरण के साथ समझा सकता है?
मुझे क्या पता है कि एक डबल अप्रत्यक्ष एक सूचक के लिए एक सूचक है। मुझे एक पॉइंटर को पॉइंटर की आवश्यकता क्यों होगी?
C में दोहरा अप्रत्यक्ष उपयोग कब किया जाना चाहिए? क्या कोई उदाहरण के साथ समझा सकता है?
मुझे क्या पता है कि एक डबल अप्रत्यक्ष एक सूचक के लिए एक सूचक है। मुझे एक पॉइंटर को पॉइंटर की आवश्यकता क्यों होगी?
जवाबों:
यदि आप वर्णों की एक सूची (एक शब्द) चाहते हैं, तो आप उपयोग कर सकते हैं 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
arr[a][b][c]
नहीं है ***arr
। पॉइंटर्स ऑफ़ पॉइंटर्स संदर्भों के संदर्भों का उपयोग करते हैं, जबकि arr[a][b][c]
पंक्ति प्रमुख क्रम में एक सामान्य सरणी के रूप में संग्रहीत किया जाता है।
एक कारण यह है कि आप किसी फ़ंक्शन को दिए गए पॉइंटर के मान को फ़ंक्शन तर्क के रूप में बदलना चाहते हैं, ऐसा करने के लिए आपको पॉइंटर को पॉइंटर की आवश्यकता होती है।
सरल शब्दों में, उपयोग **
तुम भी एक समारोह कॉल के बाहर बनाए रखने के (या में परिवर्तन को बनाए रखने) मेमोरी-आवंटन या असाइनमेंट करना चाहते हैं। (इसलिए, ऐसे फ़ंक्शन को डबल पॉइंटर arg के साथ पास करें।)
यह एक बहुत अच्छा उदाहरण नहीं हो सकता है, लेकिन आपको मूल उपयोग दिखाएगा:
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
, आप 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!!!.
को जोड़ना आशा की , प्रतिक्रिया यदि आप उदाहरण 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
, तो परिवर्तन एक अलग दायरे में कोड से संबंधित नहीं होता है।
free(p)
पर्याप्त नहीं है, आप के if(p) free(*p)
रूप में अच्छी तरह से करने की आवश्यकता है
*p
को मूल्यांकन करता है int
, इसे int
मुफ्त में पास करना () `एक बुरा विचार है।
alloc1()
एक स्मृति रिसाव शुरू करने में किया गया आवंटन । नि: शुल्क पास किए जाने वाले सूचक मान फ़ंक्शन से लौटकर खो जाते हैं।
मैंने आज इस ब्लॉग पोस्ट से एक बहुत अच्छा उदाहरण देखा , जैसा कि मैं नीचे संक्षेप में बताता हूं।
कल्पना कीजिए कि आपके पास एक लिंक्ड सूची में नोड्स के लिए एक संरचना है, जो शायद है
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. 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; }
लेकिन आप इसे डबल पॉइंटर का उपयोग किए बिना भी कर सकते हैं।
char
। एक सरणी के लिए एक पॉइंटर को char*
इस तरह उदाहरण के लिए टाइप किया जाएगा: पॉइंटर को 42 पॉइंटर से सरणी तक char(*(*p)[42])
परिभाषित करता p
है char
।
*str = ...
str
अपरिभाषित व्यवहार को गैर-संगठित रूप से लागू किया गया है।
malloc(sizeof(char) * 10);
10 पॉइंटर के लिए कमरे को आवंटित नहीं करता है char
लेकिन char
केवल 10 के लिए ..
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!)
*/
निम्नलिखित एक बहुत ही सरल 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
।)
पार्टी में थोड़ी देर हो गई, लेकिन उम्मीद है कि इससे किसी को मदद मिलेगी।
सी सरणियों में हमेशा स्टैक पर मेमोरी को आवंटित किया जाता है, इस प्रकार एक फ़ंक्शन (गैर-स्थिर) सरणी को इस तथ्य के कारण वापस नहीं किया जा सकता है कि स्टैक पर आवंटित मेमोरी स्वचालित रूप से मुक्त हो जाती है जब निष्पादन वर्तमान ब्लॉक के अंत तक पहुंच जाता है। यह वास्तव में कष्टप्रद है जब आप दो आयामी सरणियों (यानी मैट्रिसेस) से निपटना चाहते हैं और कुछ कार्यों को लागू कर सकते हैं जो मैट्रिस को बदल सकते हैं और वापस कर सकते हैं। इसे प्राप्त करने के लिए, आप डायनामिक रूप से आवंटित मेमोरी के साथ मैट्रिक्स को लागू करने के लिए पॉइंटर-टू-पॉइंटर का उपयोग कर सकते हैं:
/* 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;
}
मैंने आज डबल पॉइंटर्स का उपयोग किया है जब मैं काम के लिए कुछ प्रोग्रामिंग कर रहा था, तो मैं जवाब दे सकता हूं कि हमें उनका उपयोग क्यों करना पड़ा (यह पहली बार है जब मुझे वास्तव में डबल पॉइंटर्स का उपयोग करना था)। हमें बफ़र में निहित फ़्रेमों के वास्तविक समय एन्कोडिंग से निपटना था जो कुछ संरचनाओं के सदस्य हैं। एनकोडर में हमें उन संरचनाओं में से एक के लिए एक सूचक का उपयोग करना पड़ा। समस्या यह थी कि हमारे सूचक को दूसरे धागे से अन्य संरचनाओं को इंगित करने के लिए बदला जा रहा था। एन्कोडर में वर्तमान संरचना का उपयोग करने के लिए, मुझे एक डबल पॉइंटर का उपयोग करना था, ताकि पॉइंटर को इंगित करने के लिए जो किसी अन्य थ्रेड में संशोधित किया जा रहा था। कम से कम हमारे लिए यह स्पष्ट नहीं था कि हमें यह तरीका अपनाना था। इस प्रक्रिया में बहुत सारे पते छपे हुए थे :))।
जब आप अपने एप्लिकेशन के अन्य स्थानों पर बदले गए पॉइंटर्स पर काम करते हैं तो आप डबल पॉइंटर्स का उपयोग करते हैं। जब आप हार्डवेयर के साथ सौदा करते हैं तो आपको डबल पॉइंटर्स मिलना चाहिए।
पॉइंटर बनाम वैरिएबल के संशोधित मूल्य की तुलना करें :
#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);
...
}
double*
।