पॉइंटर्स टू पॉइंटर्स C में कैसे काम करते हैं? आप उनका उपयोग कब करेंगे?
पॉइंटर्स टू पॉइंटर्स C में कैसे काम करते हैं? आप उनका उपयोग कब करेंगे?
जवाबों:
चलो 8 बिट पते के साथ 8 बिट कंप्यूटर मान लें (और इस प्रकार केवल 256 बाइट्स मेमोरी)। यह उस मेमोरी का हिस्सा है (शीर्ष पर स्थित नंबर पते हैं):
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| | 58 | | | 63 | | 55 | | | h | e | l | l | o | \0 | |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
आप यहाँ क्या देख सकते हैं, यह है कि पते पर 63 स्ट्रिंग "हैलो" शुरू होती है। तो इस मामले में, यदि स्मृति में "हैलो" की यह एकमात्र घटना है,
const char *c = "hello";
... c
(केवल पढ़ने के लिए) स्ट्रिंग "हैलो" के लिए एक संकेतक होने के लिए परिभाषित करता है, और इस प्रकार मूल्य 63 होता है।c
खुद को कहीं संग्रहीत किया जाना चाहिए: उदाहरण 58 के स्थान पर ऊपर। बेशक हम केवल पात्रों को इंगित नहीं कर सकते हैं , लेकिन अन्य संकेत करने के लिए भी। उदाहरण के लिए:
const char **cp = &c;
अब cp
इशारा करता हैc
करता है, अर्थात्, इसमें c
(जो 58 है) का पता है। हम और भी आगे बढ़ सकते हैं। विचार करें:
const char ***cpp = &cp;
अभी cpp
का पता संग्रहीत करता है cp
। तो इसका मूल्य 55 है (ऊपर दिए गए उदाहरण के आधार पर), और आपने यह अनुमान लगाया: यह स्वयं पते 60 पर संग्रहीत है।
के रूप में क्यों एक संकेत की ओर इशारा करता है:
t
, तो सरणी के संदर्भ में प्रकार हैt *
। अब प्रकार के सरणियों की एक सरणी पर विचार करें t
: स्वाभाविक रूप से इस 2D सरणी का एक संदर्भ प्रकार (t *)*
= होगा t **
, और इसलिए एक सूचक को सूचक है।char **
।f
को प्रकार के एक तर्क को स्वीकार करने की आवश्यकता होगीt **
यदि यह एक चर को बदलना है t *
।पॉइंटर्स टू पॉइंटर्स C में कैसे काम करते हैं?
पहला पॉइंटर एक वैरिएबल है, जो किसी भी अन्य वैरिएबल की तरह है, लेकिन यह वैरिएबल का पता रखता है।
एक पॉइंटर को पॉइंटर किसी भी अन्य वैरिएबल की तरह एक वैरिएबल है, लेकिन यह वैरिएबल का पता रखता है। वह चर सिर्फ एक सूचक होता है।
आप उनका उपयोग कब करेंगे?
जब आप ढेर पर कुछ मेमोरी को पॉइंटर वापस करने की आवश्यकता होती है, तो आप उनका उपयोग कर सकते हैं, लेकिन रिटर्न वैल्यू का उपयोग नहीं करते।
उदाहरण:
int getValueOf5(int *p)
{
*p = 5;
return 1;//success
}
int get1024HeapMemory(int **p)
{
*p = malloc(1024);
if(*p == 0)
return -1;//error
else
return 0;//success
}
और आप इसे इस तरह कहते हैं:
int x;
getValueOf5(&x);//I want to fill the int varaible, so I pass it's address in
//At this point x holds 5
int *p;
get1024HeapMemory(&p);//I want to fill the int* variable, so I pass it's address in
//At this point p holds a memory address where 1024 bytes of memory is allocated on the heap
अन्य उपयोग भी हैं, जैसे कि हर C प्रोग्राम के मुख्य () तर्क में argv के लिए एक पॉइंटर का एक पॉइंटर होता है, जहाँ प्रत्येक तत्व कमांड लाइन विकल्प वाले वर्णों की एक सरणी रखता है। आपको सावधान रहना चाहिए जब आप 2 आयामी सरणियों को इंगित करने के लिए पॉइंटर्स पॉइंटर्स का उपयोग करते हैं, तो इसके बजाय सूचक को 2 आयामी सरणी का उपयोग करना बेहतर होता है।
यह खतरनाक क्यों है?
void test()
{
double **a;
int i1 = sizeof(a[0]);//i1 == 4 == sizeof(double*)
double matrix[ROWS][COLUMNS];
int i2 = sizeof(matrix[0]);//i2 == 240 == COLUMNS * sizeof(double)
}
यहाँ 2 आयामी सरणी के लिए एक पॉइंटर का एक उदाहरण दिया गया है:
int (*myPointerTo2DimArray)[ROWS][COLUMNS]
यदि आप ROWS और COLUMNS के लिए तत्वों की एक चर संख्या का समर्थन करना चाहते हैं, तो आप पॉइंटर का उपयोग 2 आयामी सरणी में नहीं कर सकते। लेकिन जब आप जानते हैं कि हाथ से पहले आप एक 2 आयामी सरणी का उपयोग करेंगे।
मुझे यह "वास्तविक दुनिया" पॉइंटर से पॉइंटर उपयोग के लिए पॉइंटर का कोड उदाहरण पसंद है, जीआईटी 2.0 में, 7 बी 1004 बी :
लिनस ने एक बार कहा था:
मैं वास्तव में चाहता हूं कि अधिक लोग कोडिंग के वास्तव में मूल निम्न-स्तरीय प्रकार को समझें। लॉकलेस नाम लुकअप की तरह बड़ा, जटिल सामान नहीं है, लेकिन केवल पॉइंटर्स-टू-पॉइंटर्स आदि का अच्छा उपयोग है।
उदाहरण के लिए, मैंने बहुत से ऐसे लोगों को देखा है जो "प्रचलित" एंट्री का ट्रैक रखते हुए एक सिंगल-लिस्टेड लिस्ट एंट्री को डिलीट कर देते हैं। , और फिर प्रविष्टि को हटाने के लिए, जैसे कुछ करना
if (prev)
prev->next = entry->next;
else
list_head = entry->next;
और जब भी मुझे उस तरह का कोड दिखाई देता है, मैं बस "यह व्यक्ति संकेत नहीं समझता"। और यह काफी दुख की बात है।
जो लोग पॉइंटर्स को समझते हैं, वे " एंट्री पॉइंटर को पॉइंटर " का उपयोग करते हैं, और लिस्ट_हेड के पते के साथ इसे इनिशियलाइज़ करते हैं। और फिर जैसे ही वे सूची को आगे बढ़ाते हैं, वे बिना किसी शर्त का उपयोग किए बिना प्रविष्टि को हटा सकते हैं, बस एक करके
*pp = entry->next
उस सरलीकरण को लागू करने से हम टिप्पणी की 2 पंक्तियों को जोड़ते हुए भी इस फ़ंक्शन से 7 लाइनें खो सकते हैं।
- struct combine_diff_path *p, *pprev, *ptmp;
+ struct combine_diff_path *p, **tail = &curr;
फिलिप ब्यूक द्वारा 2016 के वीडियो " लिनुस टॉर्वाल्ड्स डबल पॉइंटर प्रॉब्लम " की टिप्पणियों के बारे में क्रिस बताते हैं ।
कुमार टिप्पणी में बताते हैं कि ब्लॉग " लिनुस ऑन अंडरस्टैंडिंग पॉइंटर्स " है, जहां ग्रिशा ट्रुबेट्सकोय है बताते हैं:
कल्पना कीजिए कि आपके पास एक लिंक की गई सूची है:
typedef struct list_entry {
int val;
struct list_entry *next;
} list_entry;
आपको शुरुआत से अंत तक उस पर पुनरावृति करना होगा और एक विशिष्ट तत्व को निकालना होगा जिसका मूल्य to_remove के बराबर होता है।
ऐसा करने का अधिक स्पष्ट तरीका होगा:
list_entry *entry = head; /* assuming head exists and is the first entry of the list */
list_entry *prev = NULL;
while (entry) { /* line 4 */
if (entry->val == to_remove) /* this is the one to remove ; line 5 */
if (prev)
prev->next = entry->next; /* remove the entry ; line 7 */
else
head = entry->next; /* special case - first entry ; line 9 */
/* move on to the next entry */
prev = entry;
entry = entry->next;
}
हम ऊपर क्या कर रहे हैं:
- प्रविष्टि तक सूची में पुनरावृति
NULL
, जिसका अर्थ है कि हम सूची के अंत तक पहुँच चुके हैं (पंक्ति 4)।- जब हम एक प्रविष्टि भर में आते हैं तो हम हटाए गए (पंक्ति 5) चाहते हैं,
- हम वर्तमान के अगले पॉइंटर के मान को पिछले वाले को असाइन करते हैं,
- इस प्रकार वर्तमान तत्व (पंक्ति 7) को समाप्त करना।
ऊपर एक विशेष मामला है - पुनरावृत्ति की शुरुआत में कोई पिछली प्रविष्टि नहीं है (
prev
है)NULL
) नहीं है, और इसलिए सूची में पहली प्रविष्टि को हटाने के लिए आपको स्वयं को संशोधित करना होगा (पंक्ति 9)।लिनुस क्या कह रहा था कि पिछले कोड को केवल एक पॉइंटर के बजाय एक पॉइंटर को पॉइंटर बनाकर सरलीकृत किया जा सकता है ।
कोड इस तरह दिखता है:
list_entry **pp = &head; /* pointer to a pointer */
list_entry *entry = head;
while (entry) {
if (entry->val == to_remove)
*pp = entry->next;
pp = &entry->next;
entry = entry->next;
}
उपरोक्त कोड पिछले संस्करण के समान है, लेकिन ध्यान दें कि सूची के पहले तत्व के विशेष मामले के लिए हमें अब और कैसे देखने की आवश्यकता
pp
नहीं है , क्योंकि नहीं हैNULL
शुरुआत में । सरल और चतुर।इसके अलावा, उस धागे में किसी ने टिप्पणी की कि यह बेहतर है इसका कारण
*pp = entry->next
परमाणु है। यह निश्चित रूप से परमाणु नहीं है ।
उपर्युक्त अभिव्यक्ति में दो संचालक ऑपरेटर (*
और->
) और एक असाइनमेंट शामिल है, और उन तीन चीजों में से कोई भी परमाणु नहीं है।
यह एक आम गलतफहमी है, लेकिन अफसोस कि सी में कुछ भी कभी भी परमाणु ( ऑपरेटरों++
और सहित--
) होने का अनुमान नहीं लगाया जाना चाहिए !
विश्वविद्यालय में एक प्रोग्रामिंग कोर्स पर पॉइंटर्स को कवर करते समय, हमें दो संकेत दिए गए थे कि कैसे उनके बारे में सीखना शुरू किया जाए। पहली बार बिंकी के साथ पॉइंटर फन को देखना था । दूसरा है लुईस कैरोल के थ्रू द लुकिंग-ग्लास से हैडॉक्स की आंखों के मार्ग के बारे में सोचना
"आप दुखी हैं," नाइट ने चिंतित स्वर में कहा: "मुझे आपको आराम करने के लिए एक गाना गाने दो।"
"क्या यह बहुत लंबा है?" ऐलिस ने पूछा, क्योंकि उसने उस दिन कविता का एक अच्छा सौदा सुना था।
"यह लंबा है," नाइट ने कहा, "लेकिन यह बहुत, बहुत सुंदर है। हर कोई जो मुझे सुनता है उसे गाता है - या तो यह उनकी आँखों में आँसू लाता है, या फिर - "
"या फिर क्या?" एलिस ने कहा, नाइट के लिए अचानक एक ठहराव आ गया था।
"या फिर यह नहीं है, आप जानते हैं। गाने का नाम 'हेडॉक्स' आइज़ 'है। "
"ओह, यह गीत का नाम है, क्या यह है?" ऐलिस ने रुचि महसूस करने की कोशिश करते हुए कहा।
"नहीं, तुम नहीं समझते," नाइट ने कहा, थोड़ा घबराए हुए। “यही तो नाम है। नाम वास्तव में 'द एज एज एज मैन' है।
"तब मुझे यह कहना चाहिए कि 'गीत क्या कहलाता है?" एलिस ने खुद को ठीक किया।
"नहीं, आपको नहीं चाहिए: यह काफी दूसरी बात है! गीत को 'वेस एंड मीन्स' कहा जाता है: लेकिन यह केवल वही है जिसे यह कहा जाता है, आप जानते हैं! "
"अच्छा, गाना क्या है?" एलिस ने कहा, जो इस समय तक पूरी तरह से हतप्रभ था।
"मैं उस पर आ रहा था," नाइट ने कहा। "गीत वास्तव में 'ए-सिटिंग ऑन ए गेट' है: और धुन का अपना आविष्कार है।"
आप इसे पढ़ना चाहते हैं: पॉइंटर्स टू पॉइंटर्स
आशा है कि यह कुछ बुनियादी संदेहों को स्पष्ट करने में मदद करता है।
जब एक पॉइंटर के संदर्भ की आवश्यकता होती है। उदाहरण के लिए, जब आप कॉलिंग फ़ंक्शन के दायरे में घोषित पॉइंटर वैरिएबल के मान (पते को इंगित) को एक फ़ंक्शन के अंदर संशोधित करना चाहते हैं।
यदि आप किसी एकल पॉइंटर को तर्क के रूप में पास करते हैं, तो आप पॉइंटर की स्थानीय प्रतियों को संशोधित करेंगे, न कि कॉलिंग स्कोप में मूल पॉइंटर। एक पॉइंटर को पॉइंटर के साथ, आप बाद को संशोधित करते हैं।
एक पॉइंटर को एक पॉइंटर को एक हैंडल भी कहा जाता है । इसका एक उपयोग अक्सर तब होता है जब किसी ऑब्जेक्ट को मेमोरी में स्थानांतरित किया जा सकता है या हटाया जा सकता है। एक अक्सर ऑब्जेक्ट के उपयोग को लॉक और अनलॉक करने के लिए जिम्मेदार होता है, इसलिए इसे एक्सेस करते समय इसे स्थानांतरित नहीं किया जाएगा।
इसका उपयोग अक्सर मेमोरी प्रतिबंधित वातावरण अर्थात पाम ओएस में किया जाता है।
इस अवधारणा को बेहतर समझने के लिए नीचे दिए गए आंकड़े और कार्यक्रम पर विचार करें ।
चित्र के अनुसार, ptr1 एक एकल सूचक है जिसमें चर संख्या का पता है ।
ptr1 = #
इसी तरह ptr2 पॉइंटर (डबल पॉइंटर) का एक पॉइंटर है जो पॉइंटर ptr1 का एड्रेस है ।
ptr2 = &ptr1;
एक पॉइंटर जो दूसरे पॉइंटर की ओर इशारा करता है उसे डबल पॉइंटर के रूप में जाना जाता है। इस उदाहरण में ptr2 एक डबल पॉइंटर है।
उपरोक्त आरेख से मान:
Address of variable num has : 1000
Address of Pointer ptr1 is: 2000
Address of Pointer ptr2 is: 3000
उदाहरण:
#include <stdio.h>
int main ()
{
int num = 10;
int *ptr1;
int **ptr2;
// Take the address of var
ptr1 = #
// Take the address of ptr1 using address of operator &
ptr2 = &ptr1;
// Print the value
printf("Value of num = %d\n", num );
printf("Value available at *ptr1 = %d\n", *ptr1 );
printf("Value available at **ptr2 = %d\n", **ptr2);
}
आउटपुट:
Value of num = 10
Value available at *ptr1 = 10
Value available at **ptr2 = 10
यह पॉइंटर के एड्रेस वैल्यू का पॉइंटर है। (मुझे पता है कि भयानक है)
मूल रूप से, यह आपको एक पॉइंटर को किसी अन्य पॉइंटर के पते के मान तक ले जाने देता है, इसलिए आप संशोधित कर सकते हैं कि कोई अन्य पॉइंटर उप फ़ंक्शन से इंगित कर रहा है, जैसे:
void changeptr(int** pp)
{
*pp=&someval;
}
पॉइंटर टू पॉइंटर, पॉइंटर, पॉइंटर टू पॉइंटर है।
SomeType ** का एक अर्थपूर्ण उदाहरण ** एक बोली-संबंधी सरणी है: आपके पास एक सरणी है, जो अन्य सरणियों की ओर संकेत करती है, इसलिए जब आप लिखते हैं
dpointer [5] [6]
आप उस सरणी पर पहुंचते हैं, जिसमें उसकी 5 वीं स्थिति में अन्य सरणियों के संकेत होते हैं, पॉइंटर प्राप्त करें (उसके नाम fpointer दें) और फिर उस सरणी के संदर्भित सरणी के 6 वें तत्व तक पहुंचें (इसलिए, fpointer [6])।
यह कैसे काम करता है: यह एक वैरिएबल है जो दूसरे पॉइंटर को स्टोर कर सकता है।
आप उनका उपयोग कब करेंगे: उनमें से कई का उपयोग करता है यदि आपका फ़ंक्शन किसी सरणी का निर्माण करना चाहता है और उसे कॉलर को वापस करना चाहता है।
//returns the array of roll nos {11, 12} through paramater
// return value is total number of students
int fun( int **i )
{
int *j;
*i = (int*)malloc ( 2*sizeof(int) );
**i = 11; // e.g., newly allocated memory 0x2000 store 11
j = *i;
j++;
*j = 12; ; // e.g., newly allocated memory 0x2004 store 12
return 2;
}
int main()
{
int *i;
int n = fun( &i ); // hey I don't know how many students are in your class please send all of their roll numbers.
for ( int j=0; j<n; j++ )
printf( "roll no = %d \n", i[j] );
return 0;
}
मैंने एक 5 मिनट का वीडियो बनाया है जो बताता है कि पॉइंटर्स कैसे काम करते हैं:
वहाँ इतने सारे उपयोगी स्पष्टीकरण, लेकिन मुझे नहीं मिला बस एक छोटा विवरण, इसलिए ।।
मूल रूप से पॉइंटर चर का पता है। संक्षिप्त सारांश कोड:
int a, *p_a;//declaration of normal variable and int pointer variable
a = 56; //simply assign value
p_a = &a; //save address of "a" to pointer variable
*p_a = 15; //override the value of the variable
//print 0xfoo and 15
//- first is address, 2nd is value stored at this address (that is called dereference)
printf("pointer p_a is having value %d and targeting at variable value %d", p_a, *p_a);
इसके अलावा उपयोगी जानकारी विषय और संदर्भ का अर्थ क्या है में पाया जा सकता है
और मुझे इतना यकीन नहीं है, जब पॉइंटर्स उपयोगी हो सकते हैं, लेकिन आम तौर पर जब आप कुछ मैनुअल / डायनेमिक मेमोरी आवंटन- मॉलोक, कॉलोक, आदि कर रहे हैं, तो उनका उपयोग करना आवश्यक है ।
इसलिए मुझे उम्मीद है कि यह समस्याग्रस्त को स्पष्ट करने में भी मदद करेगा :)