C Language
संकेत
खोज…
परिचय
पॉइंटर एक प्रकार का वैरिएबल है जो किसी अन्य ऑब्जेक्ट या फ़ंक्शन के पते को स्टोर कर सकता है।
वाक्य - विन्यास
- <डेटा प्रकार> * <परिवर्तनीय नाम>;
- int * ptrToInt;
- शून्य * ptrToVoid; / * C89 + * /
- स्ट्रक्चर कुछ स्ट्रोक्ट * ptrToStruct;
- int ** ptrToPtrToInt;
- int गिरफ्तारी [लंबाई]; int * ptrToFirstElem = arrest; / * के लिए <C99 'लंबाई' के लिए एक संकलन समय स्थिर होना चाहिए,> = C11 के लिए इसे एक होना चाहिए। * /
- int * arrayOfPtrsToInt [लंबाई]; / * के लिए <C99 'लंबाई' के लिए एक संकलन समय स्थिर होना चाहिए,> = C11 के लिए इसे एक होना चाहिए। * /
टिप्पणियों
तारांकन की स्थिति परिभाषा के अर्थ को प्रभावित नहीं करती है:
/* The * operator binds to right and therefore these are all equivalent. */
int *i;
int * i;
int* i;
हालाँकि, एक साथ कई बिंदुओं को परिभाषित करते समय, प्रत्येक को अपने स्वयं के तार की आवश्यकता होती है:
int *i, *j; /* i and j are both pointers */
int* i, j; /* i is a pointer, but j is an int not a pointer variable */
पॉइंटर्स की एक सारणी भी संभव है, जहाँ ऐरे चर नाम के पहले एक तारांकन दिया जाता है:
int *foo[2]; /* foo is a array of pointers, can be accessed as *foo[0] and *foo[1] */
आम त्रुटियों
पॉइंटर्स का अनुचित उपयोग अक्सर बग का एक स्रोत होता है जिसमें सुरक्षा बग या प्रोग्राम क्रैश शामिल हो सकते हैं, जो अक्सर विभाजन दोष के कारण होते हैं।
आवंटन विफलताओं के लिए जाँच नहीं
मेमोरी आवंटन सफल होने की गारंटी नहीं है, और इसके बजाय एक NULL
पॉइंटर लौटा सकता है। लौटाए गए मान का उपयोग करते हुए, जाँच किए बिना कि आवंटन सफल है, अपरिभाषित व्यवहार को आमंत्रित करता है । यह आमतौर पर एक दुर्घटना की ओर जाता है, लेकिन इस बात की कोई गारंटी नहीं है कि एक दुर्घटना इतनी निर्भर होगी कि समस्या भी पैदा हो सकती है।
उदाहरण के लिए, असुरक्षित तरीका:
struct SomeStruct *s = malloc(sizeof *s);
s->someValue = 0; /* UNSAFE, because s might be a null pointer */
सुरक्षित तरीका:
struct SomeStruct *s = malloc(sizeof *s);
if (s)
{
s->someValue = 0; /* This is safe, we have checked that s is valid */
}
मेमोरी का अनुरोध करते समय आकार के बजाय शाब्दिक संख्याओं का उपयोग करना
किसी दिए गए संकलक / मशीन विन्यास के लिए, प्रकारों का ज्ञात आकार होता है; हालाँकि, ऐसा कोई मानक नहीं है जो यह परिभाषित करता हो कि किसी दिए गए प्रकार ( char
) का आकार सभी संकलक / मशीन विन्यास के लिए समान होगा। यदि मेमोरी मेमोरी आवंटन के लिए कोड sizeof(int)
बजाय 4 का उपयोग करता है, तो यह मूल मशीन पर काम कर सकता है, लेकिन कोड आवश्यक रूप से अन्य मशीनों या कंपाइलरों के लिए पोर्टेबल नहीं है। प्रकारों के लिए निश्चित आकार को sizeof(that_type)
या sizeof(*var_ptr_to_that_type)
द्वारा प्रतिस्थापित किया जाना चाहिए।
गैर-पोर्टेबल आवंटन:
int *intPtr = malloc(4*1000); /* allocating storage for 1000 int */
long *longPtr = malloc(8*1000); /* allocating storage for 1000 long */
पोर्टेबल आवंटन:
int *intPtr = malloc(sizeof(int)*1000); /* allocating storage for 1000 int */
long *longPtr = malloc(sizeof(long)*1000); /* allocating storage for 1000 long */
या, अभी भी बेहतर:
int *intPtr = malloc(sizeof(*intPtr)*1000); /* allocating storage for 1000 int */
long *longPtr = malloc(sizeof(*longPtr)*1000); /* allocating storage for 1000 long */
स्म्रति से रिसाव
free
का उपयोग करके मेमोरी को डी-आवंटित करने में विफलता गैर-पुन: प्रयोज्य मेमोरी के बिल्डअप की ओर ले जाती है, जो अब प्रोग्राम द्वारा उपयोग नहीं की जाती है; इसे मेमोरी लीक कहा जाता है। मेमोरी लीक स्मृति संसाधनों को बेकार कर देती है और आवंटन विफलताओं को जन्म दे सकती है।
तार्किक त्रुटियाँ
सभी आवंटन समान पैटर्न का पालन करना चाहिए:
- का उपयोग कर आवंटन
malloc
(याcalloc
) - डेटा स्टोर करने के लिए उपयोग
- डी-आवंटन
free
का उपयोग कर
इस पैटर्न का पालन करने में विफलता, जैसे कि कॉल टू free
( डैंगलिंग पॉइंटर ) के बाद मेमोरी का उपयोग करना या malloc
( वाइल्ड पॉइंटर ) पर कॉल करने से पहले, दो बार free
कॉल करना ("डबल फ्री"), आदि, आमतौर पर एक विभाजन दोष का कारण बनता है। कार्यक्रम के दुर्घटना में परिणाम।
ये त्रुटियां क्षणिक हो सकती हैं और डीबग करना कठिन हो सकता है - उदाहरण के लिए, मुक्त की गई मेमोरी को आमतौर पर ओएस द्वारा तुरंत पुनर्प्राप्त नहीं किया जाता है, और इस तरह झूलने वाले पॉइंटर्स थोड़ी देर के लिए बने रह सकते हैं और काम करने के लिए प्रकट होते हैं।
सिस्टम पर जहां यह काम करता है, वल्ग्रिंड यह पहचानने के लिए एक अमूल्य उपकरण है कि क्या मेमोरी लीक हुई है और इसे मूल रूप से कहां आवंटित किया गया था।
वेरिएबल को ढेर करने के लिए पॉइंटर्स बनाना
पॉइंटर बनाना वैरिएबल के जीवन को इंगित नहीं करता है। उदाहरण के लिए:
int* myFunction()
{
int x = 10;
return &x;
}
यहाँ, x
में स्वचालित भंडारण अवधि है (जिसे आमतौर पर स्टैक आवंटन के रूप में जाना जाता है)। क्योंकि यह स्टैक पर आवंटित किया गया है, इसका जीवनकाल केवल तब तक है जब तक myFunction
निष्पादित हो रहा है; myFunction
से बाहर निकलने के बाद, चर x
नष्ट हो जाता है। इस फ़ंक्शन को x
(का उपयोग करके &x
) का पता मिलता है, और कॉलर को एक गैर-मौजूद चर के लिए सूचक के साथ छोड़कर, कॉलर को वापस कर देता है। इस चर का उपयोग करने का प्रयास तब अपरिभाषित व्यवहार को लागू करेगा।
अधिकांश कंपाइलर वास्तव में फ़ंक्शन से बाहर निकलने के बाद एक स्टैक फ्रेम को साफ़ नहीं करते हैं, इस प्रकार रिटर्न पॉइंटर को डीरफेर करना अक्सर आपको अपेक्षित डेटा देता है। जब एक अन्य फ़ंक्शन को बुलाया जाता है, तो इंगित की जा रही मेमोरी को ओवरराइट किया जा सकता है, और ऐसा प्रतीत होता है कि जिस डेटा को इंगित किया जा रहा है वह दूषित है।
इसके समाधान के लिए, या तो malloc
के लिए चर वापस करने भंडारण, और नव निर्मित भंडारण के लिए एक सूचक लौटने, या की आवश्यकता है कि एक वैध सूचक बजाय वापस लौटाते समय, उदाहरण के लिए के समारोह में पारित हो जाता है:
#include <stdlib.h>
#include <stdio.h>
int *solution1(void)
{
int *x = malloc(sizeof *x);
if (x == NULL)
{
/* Something went wrong */
return NULL;
}
*x = 10;
return x;
}
void solution2(int *x)
{
/* NB: calling this function with an invalid or null pointer
causes undefined behaviour. */
*x = 10;
}
int main(void)
{
{
/* Use solution1() */
int *foo = solution1();
if (foo == NULL)
{
/* Something went wrong */
return 1;
}
printf("The value set by solution1() is %i\n", *foo);
/* Will output: "The value set by solution1() is 10" */
free(foo); /* Tidy up */
}
{
/* Use solution2() */
int bar;
solution2(&bar);
printf("The value set by solution2() is %i\n", bar);
/* Will output: "The value set by solution2() is 10" */
}
return 0;
}
इंक्रीमेंटिंग / डीक्रिमेंटिंग और डीरेफेरेंसिंग
आप लिखते हैं तो *p++
बढ़ाने के लिए क्या द्वारा बताया है p
, आप गलत हैं।
पोस्ट इन्क्रीमेंटिंग / डीक्रिमेंटिंग को डीरेफरिंग से पहले निष्पादित किया जाता है। इसलिए, यह अभिव्यक्ति पॉइंटर p
बढ़ाएगा और जो इसे बिना बदले में बढ़ाए जाने से पहले p
द्वारा इंगित किया गया था उसे वापस लौटाएगा।
आप लिखना चाहिए (*p)++
बढ़ाने के लिए क्या द्वारा बताया है p
।
यह नियम *p--
पोस्ट करने के लिए भी लागू होता है: *p--
पॉइंटर p
स्वयं *p--
, p
द्वारा इंगित नहीं किया गया है।
एक सूचक को संदर्भित करना
int a = 1;
int *a_pointer = &a;
a_pointer
को dereference करने और a का मान बदलने के लिए, हम निम्नलिखित ऑपरेशन का उपयोग करते हैं
*a_pointer = 2;
इसे निम्न प्रिंट स्टेटमेंट का उपयोग करके सत्यापित किया जा सकता है।
printf("%d\n", a); /* Prints 2 */
printf("%d\n", *a_pointer); /* Also prints 2 */
हालांकि, एक एक भिन्नता के लिए गलत होगा NULL
या यूं कहें कि अमान्य सूचक। इस
int *p1, *p2;
p1 = (int *) 0xbad;
p2 = NULL;
*p1 = 42;
*p2 = *p1 + 1;
आमतौर पर अपरिभाषित व्यवहार है । p1
को 0xbad
नहीं किया जा सकता है क्योंकि यह एक एड्रेस 0xbad
जो एक मान्य एड्रेस नहीं हो सकता है। कौन जानता है कि वहाँ क्या है यह ऑपरेटिंग सिस्टम मेमोरी, या किसी अन्य प्रोग्राम की मेमोरी हो सकती है। इस तरह का एकमात्र समय कोड उपयोग किया जाता है, एम्बेडेड विकास में है, जो विशेष जानकारी को हार्ड-कोडित पते पर संग्रहीत करता है। p2
को dereferenced नहीं जा सकता क्योंकि यह NULL
, जो अमान्य है।
एक संरचना की ओर इशारा करते हुए
मान लें कि हमारे पास निम्नलिखित संरचना है:
struct MY_STRUCT
{
int my_int;
float my_float;
};
हम परिभाषित कर सकते हैं MY_STRUCT
छोड़ struct
तो हम टाइप करने के लिए की जरूरत नहीं है कीवर्ड struct MY_STRUCT
हर बार जब हम इसका इस्तेमाल। यह, हालांकि, वैकल्पिक है।
typedef struct MY_STRUCT MY_STRUCT;
अगर हमारे पास इस संरचना के उदाहरण के लिए एक संकेतक है
MY_STRUCT *instance;
यदि यह कथन फ़ाइल स्कोप पर दिखाई देता है, तो प्रोग्राम शुरू होने पर एक अशक्त सूचक के साथ instance
को आरंभ किया जाएगा। यदि यह कथन किसी फ़ंक्शन के अंदर दिखाई देता है, तो इसका मान अपरिभाषित है। वेरिएबल को एक वैध MY_STRUCT
वैरिएबल को इंगित करने के लिए या गतिशील रूप से आवंटित स्थान पर इंगित किया जाना चाहिए, इससे पहले कि इसे MY_STRUCT
किया जा सके। उदाहरण के लिए:
MY_STRUCT info = { 1, 3.141593F };
MY_STRUCT *instance = &info;
जब पॉइंटर मान्य होता है, तो हम दो अलग-अलग नोटेशनों में से एक का उपयोग करके अपने सदस्यों तक पहुंचने के लिए इसे स्थगित कर सकते हैं:
int a = (*instance).my_int;
float b = instance->my_float;
जबकि ये दोनों विधियाँ काम करती हैं, यह कोष्ठक के संयोजन के बजाय तीर ->
ऑपरेटर का उपयोग करने के लिए बेहतर अभ्यास है, dereference *
ऑपरेटर और डॉट .
ऑपरेटर क्योंकि यह पढ़ना और समझना आसान है, विशेष रूप से नेस्टेड उपयोगों के साथ।
एक और महत्वपूर्ण अंतर नीचे दिखाया गया है:
MY_STRUCT copy = *instance;
copy.my_int = 2;
इस मामले में, copy
में instance
की सामग्री की एक प्रति शामिल है। my_int
की copy
बदलने से यह instance
में नहीं बदलेगा।
MY_STRUCT *ref = instance;
ref->my_int = 2;
इस मामले में, ref
instance
लिए एक संदर्भ है। संदर्भ का उपयोग करके my_int
को बदलना इसे instance
में बदल देगा।
कार्यों के मापदंडों के रूप में संरचनाओं के बजाय पॉइंटर्स का उपयोग करना आम बात है, न कि स्वयं संरचनाओं के बजाय। फंक्शन पैरामीटर्स के रूप में स्ट्रक्चर्स का उपयोग करने से स्टैक ओवरफ्लो का कारण बन सकता है यदि स्ट्रक्चर बड़ा हो। किसी पॉइंटर को किसी पॉइंटर का उपयोग करने से केवल पॉइंटर के लिए पर्याप्त स्टैक स्पेस का उपयोग किया जाता है, लेकिन यदि फ़ंक्शन उस संरचना को बदलता है जो फ़ंक्शन में पारित हो जाता है, तो साइड इफेक्ट हो सकता है।
कार्य बिंदु
पॉइंटर्स का उपयोग फ़ंक्शंस पर इंगित करने के लिए भी किया जा सकता है।
आइए एक बुनियादी कार्य करें:
int my_function(int a, int b) { return 2 * a + 3 * b; }
अब, उस फ़ंक्शन के प्रकार को इंगित करते हैं:
int (*my_pointer)(int, int);
एक बनाने के लिए, बस इस टेम्पलेट का उपयोग करें:
return_type_of_func (*my_func_pointer)(type_arg1, type_arg2, ...)
फिर हमें इस पॉइंटर को फंक्शन में असाइन करना होगा:
my_pointer = &my_function;
इस सूचक को अब फ़ंक्शन को कॉल करने के लिए उपयोग किया जा सकता है:
/* Calling the pointed function */ int result = (*my_pointer)(4, 2); ... /* Using the function pointer as an argument to another function */ void another_function(int (*another_pointer)(int, int)) { int a = 4; int b = 2; int result = (*another_pointer)(a, b); printf("%d\n", result); }
यद्यपि यह वाक्यविन्यास बुनियादी प्रकारों के साथ अधिक प्राकृतिक और सुसंगत लगता है, लेकिन फ़ंक्शन और डीरफ्रेंसिंग फ़ंक्शन पॉइंटर्स के लिए &
*
ऑपरेटरों के उपयोग की आवश्यकता नहीं होती है। तो निम्नलिखित स्निपेट समान रूप से मान्य है:
/* Attribution without the & operator */ my_pointer = my_function; /* Dereferencing without the * operator */ int result = my_pointer(4, 2);
फ़ंक्शन पॉइंटर्स की पठनीयता को बढ़ाने के लिए, टाइप्डिफ का उपयोग किया जा सकता है।
typedef void (*Callback)(int a); void some_function(Callback callback) { int a = 4; callback(a); }
एक और पठनीयता की चाल यह है कि सी मानक किसी को फ़ंक्शन प्रोटोटाइप में एक फ़ंक्शन पॉइंटर को सरल बनाने की अनुमति देता है, जैसे कि फ़ंक्शन प्रोटोटाइप की तरह दिखने वाली चीज़ के लिए ऊपर (लेकिन परिवर्तनीय घोषणा में नहीं); इस प्रकार निम्नलिखित को फ़ंक्शन परिभाषाओं और घोषणाओं के लिए समान रूप से उपयोग किया जा सकता है:
void some_function(void callback(int))
{
int a = 4;
callback(a);
}
यह सभी देखें
प्रारंभिक बिंदु
जंगली संकेत से बचने के लिए पॉइंटर इनिशियलाइज़ेशन एक अच्छा तरीका है। आरंभीकरण सरल है और एक चर के आरंभीकरण से अलग नहीं है।
#include <stddef.h>
int main()
{
int *p1 = NULL;
char *p2 = NULL;
float *p3 = NULL;
/* NULL is a macro defined in stddef.h, stdio.h, stdlib.h, and string.h */
...
}
ज्यादातर ऑपरेटिंग सिस्टम में, अनजाने में NULL
लिए इनिशियलाइज़ किए गए पॉइंटर का उपयोग करने से अक्सर प्रोग्राम क्रैश हो जाएगा, जिससे समस्या के कारण की पहचान करना आसान हो जाएगा। एक असिंचित पॉइंटर का उपयोग करना अक्सर हार्ड-टू-डायग्नोसिस बग का कारण बन सकता है।
सावधान:
NULL
पॉइंटर को डीफ़र करने का परिणाम अपरिभाषित है, इसलिए यह जरूरी नहीं होगा कि प्रोग्राम के चल रहे ऑपरेटिंग सिस्टम का स्वाभाविक व्यवहार होने पर भी क्रैश हो जाए। कंपाइलर ऑप्टिमाइज़ेशन क्रैश का कारण हो सकता है, स्रोत कोड में बिंदु से पहले या बाद में क्रैश होने का कारण हो सकता है, जिस पर नल पॉइंटर डेरेफेरेंस हुआ, या कोड के कुछ हिस्सों का कारण बनता है जिसमें प्रोग्राम से अनपेक्षित रूप से हटाए जाने के लिए नल पॉइंटर डेरेफेरेंस होता है। डिबग बिल्ड आमतौर पर इन व्यवहारों को प्रदर्शित नहीं करेगा, लेकिन यह भाषा मानक द्वारा गारंटी नहीं है। अन्य अप्रत्याशित और / या अवांछनीय व्यवहार की भी अनुमति है।
क्योंकि NULL
कभी भी एक चर, आबंटित मेमोरी या किसी फंक्शन को इंगित नहीं करता है, यह एक गार्ड वैल्यू के रूप में उपयोग करना सुरक्षित है।
सावधान:
आमतौर पर NULL
को (void *)0
रूप में परिभाषित किया गया है। लेकिन इसका मतलब यह नहीं है कि निर्दिष्ट स्मृति पता 0x0
। अधिक स्पष्टीकरण के लिए NULL पॉइंटर्स के लिए C-faq का संदर्भ लें
ध्यान दें कि आप NULL के अलावा अन्य मान रखने के लिए पॉइंटर्स को इनिशियलाइज़ भी कर सकते हैं।
int i1;
int main()
{
int *p1 = &i1;
const char *p2 = "A constant string to point to";
float *p3 = malloc(10 * sizeof(float));
}
संचालक का पता (&)
किसी भी वस्तु (यानी, वैरिएबल, एरे, यूनियन, स्ट्रक्चर, पॉइंटर या फंक्शन) के लिए यूनिरी एड्रेस ऑपरेटर का उपयोग उस ऑब्जेक्ट के पते तक पहुंचने के लिए किया जा सकता है।
मान लो कि
int i = 1;
int *p = NULL;
तो फिर एक कथन p = &i;
, पॉइंटर p
के वेरिएबल i
के एड्रेस को कॉपी करता है।
यह के रूप में व्यक्त किया है p
करने के लिए अंक i
।
printf("%d\n", *p);
प्रिंट 1, जो i
का मान है।
सूचक अंकगणित
कृपया यहाँ देखें: सूचक अंकगणित
शून्य * मानक कार्यों के लिए तर्क और वापसी मूल्यों के रूप में इंगित करता है
void*
एक प्रकार से पॉइंट टू ऑब्जेक्ट प्रकार के लिए सभी प्रकार की पकड़ है। उपयोग में इसका एक उदाहरण malloc
फ़ंक्शन के साथ है, जिसे घोषित किया गया है
void* malloc(size_t);
पॉइंटर-टू-वॉयड रिटर्न प्रकार का अर्थ है कि malloc
से पॉइंटर को किसी अन्य प्रकार की वस्तु पर रिटर्न मान निर्दिष्ट करना संभव है:
int* vector = malloc(10 * sizeof *vector);
आमतौर पर यह माना जाता है कि शून्य बिंदुओं में और बाहर मूल्यों को स्पष्ट रूप से नहीं डालना अच्छा अभ्यास है। malloc()
विशिष्ट मामले में malloc()
यह इसलिए है क्योंकि एक स्पष्ट कलाकारों के साथ, कंपाइलर अन्यथा मान सकता है, लेकिन इसके बारे में चेतावनी नहीं देता है, अगर आप stdlib.h
लिए गलत रिटर्न प्रकार malloc()
, यदि आप stdlib.h
को शामिल करना भूल जाते हैं। यह DRY के बेहतर अनुरूप (खुद को न दोहराएं) सिद्धांत के अनुरूप शून्य बिंदुओं के सही व्यवहार का उपयोग करने का भी मामला है; उपरोक्त की तुलना निम्न से करें, जिसमें निम्नलिखित कोड में कई अनावश्यक अतिरिक्त स्थान होते हैं जहाँ टाइपो समस्याएँ पैदा कर सकता है:
int* vector = (int*)malloc(10 * sizeof int*);
इसी प्रकार, जैसे कार्य
void* memcpy(void *restrict target, void const *restrict source, size_t size);
उनके तर्क को void *
रूप में निर्दिष्ट किया गया void *
क्योंकि किसी भी वस्तु का पता, प्रकार की परवाह किए बिना, इसमें पारित किया जा सकता है। यहां भी, एक कॉल कास्ट का उपयोग नहीं करना चाहिए।
unsigned char buffer[sizeof(int)];
int b = 67;
memcpy(buffer, &b, sizeof buffer);
कांस्टेबल पॉइंटर्स
सिंगल पॉइंटर्स
एक
int
कोint
सूचक अलग-अलग पूर्णांक को इंगित कर सकता है और सूचक के माध्यम से
int
को बदला जा सकता है। करने के लिए बात करने के लिए ख कोड प्रदान करती है की यह नमूनाint b
बदलतीb
के लिए के मूल्य100
।int b; int* p; p = &b; /* OK */ *p = 100; /* OK */
एक
const int
सूचक अलग-अलग पूर्णांक को इंगित कर सकता है लेकिन सूचक के माध्यम से
int
का मान नहीं बदला जा सकता है।int b; const int* p; p = &b; /* OK */ *p = 100; /* Compiler Error */
int
कोconst
पॉइंटरपॉइंटर केवल एक
int
को इंगित कर सकता है लेकिन पॉइंटर के माध्यम सेint
का मान बदला जा सकता है।int a, b; int* const p = &b; /* OK as initialisation, no assignment */ *p = 100; /* OK */ p = &a; /* Compiler Error */
const
सूचक toconst int
प्वाइंटर केवल एक
int
को इंगित कर सकता है और पॉइंटर के माध्यम सेint
को नहीं बदला जा सकता है।int a, b; const int* const p = &b; /* OK as initialisation, no assignment */ p = &a; /* Compiler Error */ *p = 100; /* Compiler Error */
सूचक को इंगित करने के लिए
एक सूचक के लिए एक सूचक को
int
यह कोड
p1
को डबल पॉइंटरp
(जो तबint* p1
(जोint
को इंगित करता है) की ओर इंगित करता है) का पता प्रदान करता है।तब
int a
को इंगित करने के लिएp1
को बदलता हैint a
। फिर 100 के मान को बदल देता है।void f1(void) { int a, b; int *p1; int **p; p1 = &b; /* OK */ p = &p1; /* OK */ *p = &a; /* OK */ **p = 100; /* OK */ }
पॉइंटर से पॉइंटर टू
const int
void f2(void) { int b; const int *p1; const int **p; p = &p1; /* OK */ *p = &b; /* OK */ **p = 100; /* error: assignment of read-only location ‘**p’ */ }
करने के लिए सूचक
const
एक सूचकint
void f3(void) { int b; int *p1; int * const *p; p = &p1; /* OK */ *p = &b; /* error: assignment of read-only location ‘*p’ */ **p = 100; /* OK */ }
const
सूचक को सूचक कोint
void f4(void) { int b; int *p1; int ** const p = &p1; /* OK as initialisation, not assignment */ p = &p1; /* error: assignment of read-only variable ‘p’ */ *p = &b; /* OK */ **p = 100; /* OK */ }
पॉइंटर टू
const
पॉइंटर टूconst int
void f5(void) { int b; const int *p1; const int * const *p; p = &p1; /* OK */ *p = &b; /* error: assignment of read-only location ‘*p’ */ **p = 100; /* error: assignment of read-only location ‘**p’ */ }
const
सूचक सूचक toconst int
void f6(void) { int b; const int *p1; const int ** const p = &p1; /* OK as initialisation, not assignment */ p = &p1; /* error: assignment of read-only variable ‘p’ */ *p = &b; /* OK */ **p = 100; /* error: assignment of read-only location ‘**p’ */ }
int
लिएconst
पॉइंटर सेconst
पॉइंटरvoid f7(void) { int b; int *p1; int * const * const p = &p1; /* OK as initialisation, not assignment */ p = &p1; /* error: assignment of read-only variable ‘p’ */ *p = &b; /* error: assignment of read-only location ‘*p’ */ **p = 100; /* OK */ }
एक ही तारांकन चिह्न, विभिन्न अर्थ
परिसर
C और C ++ में पॉइंटर सिंटैक्स के आसपास की सबसे भ्रामक बात यह है कि वास्तव में दो अलग-अलग अर्थ हैं जो पॉइंटर प्रतीक, तारांकन ( *
) के साथ लागू होते हैं, एक चर के साथ प्रयोग किया जाता है।
उदाहरण
सबसे पहले, आप पॉइंटर चर घोषित करने के लिए *
का उपयोग करते हैं ।
int i = 5;
/* 'p' is a pointer to an integer, initialized as NULL */
int *p = NULL;
/* '&i' evaluates into address of 'i', which then assigned to 'p' */
p = &i;
/* 'p' is now holding the address of 'i' */
जब आप घोषित नहीं कर रहे हैं (या गुणा कर रहे हैं), *
का प्रयोग पॉइंटर वेरिएबल को डिरेल करने के लिए किया जाता है:
*p = 123;
/* 'p' was pointing to 'i', so this changes value of 'i' to 123 */
जब आप एक मौजूदा पॉइंटर वैरिएबल को दूसरे वैरिएबल का पता रखना चाहते हैं , तो आप *
उपयोग नहीं करते, लेकिन इसे इस तरह करें:
p = &another_variable;
सी-प्रोग्रामिंग न्यूबीज़ के बीच एक आम भ्रम तब पैदा होता है जब वे एक ही समय में एक पॉइंटर वैरिएबल की घोषणा करते हैं और शुरू करते हैं।
int *p = &i;
चूंकि int i = 5;
और int i; i = 5;
एक ही परिणाम दे, उनमें से कुछ ने सोचा हो सकता है int *p = &i;
और int *p; *p = &i;
एक ही परिणाम भी दें। तथ्य यह है, नहीं, int *p; *p = &i;
एक अनइंस्टॉल किए गए पॉइंटर को टालने का प्रयास करेगा जिसके परिणामस्वरूप यूबी होगा। कभी भी उपयोग न करें *
जब आप घोषित नहीं कर रहे हैं और न ही एक पॉइंटर को डीरफ्रेंस कर रहे हैं।
निष्कर्ष
तारांकन ( *
) का संकेत बिंदुओं के संबंध में C के भीतर दो अलग-अलग अर्थ हैं, यह इस बात पर निर्भर करता है कि इसका उपयोग कहां किया गया है। जब एक चर घोषणा के भीतर उपयोग किया जाता है, तो समतुल्य पक्ष के दाईं ओर का मान मेमोरी में एक पते के लिए एक सूचक मान होना चाहिए। जब पहले से घोषित चर के साथ उपयोग किया जाता है, तो तारांकन सूचक मान को धीमा कर देगा, स्मृति में इंगित करने के लिए इसका अनुसरण करते हुए, और वहां संग्रहीत मूल्य को असाइन या पुनर्प्राप्त करने की अनुमति देगा।
ले जाओ
अपने पी और क्यू का ध्यान रखना महत्वपूर्ण है, इसलिए बात करते समय, पॉइंटर्स के साथ व्यवहार करना। जब आप तारांकन का उपयोग कर रहे हों, और जब आप इसका उपयोग करते हैं तो इसका क्या अर्थ होता है, इसके प्रति सावधान रहें। इस छोटे से विवरण को देखने से आपको छोटी-छोटी बातों और / या अपरिभाषित व्यवहार का सामना करना पड़ सकता है जिनसे आप वास्तव में निपटना नहीं चाहते हैं।
सूचक को इंगित करने के लिए
सी में, एक पॉइंटर दूसरे पॉइंटर को संदर्भित कर सकता है।
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int A = 42;
int* pA = &A;
int** ppA = &pA;
int*** pppA = &ppA;
printf("%d", ***pppA); /* prints 42 */
return EXIT_SUCCESS;
}
लेकिन, संदर्भ-और-संदर्भ को सीधे अनुमति नहीं है।
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int A = 42;
int* pA = &A;
int** ppA = &&A; /* Compilation error here! */
int*** pppA = &&&A; /* Compilation error here! */
...
परिचय
एक पॉइंटर को किसी भी अन्य वैरिएबल की तरह घोषित किया जाता है, सिवाय इसके कि तार को इंगित करने के लिए तारांकन चिह्न ( *
) को टाइप और वेरिएबल के नाम के बीच रखा जाता है।
int *pointer; /* inside a function, pointer is uninitialized and doesn't point to any valid object yet */
एक ही प्रकार के दो सूचक चर घोषित करने के लिए, एक ही घोषणा में, प्रत्येक पहचानकर्ता से पहले तारांकन चिह्न का उपयोग करें। उदाहरण के लिए,
int *iptr1, *iptr2;
int *iptr3, iptr4; /* iptr3 is a pointer variable, whereas iptr4 is misnamed and is an int */
एम्परसेंड ( &
) द्वारा निरूपित पता या संदर्भ ऑपरेटर किसी दिए गए चर का पता देता है जिसे उपयुक्त प्रकार के पॉइंटर में रखा जा सकता है।
int value = 1;
pointer = &value;
एक तारांकन चिह्न ( *
) द्वारा निरूपित अप्रत्यक्ष या परिशोधन ऑपरेटर एक सूचक द्वारा इंगित की गई वस्तु की सामग्री प्राप्त करता है।
printf("Value of pointed to integer: %d\n", *pointer);
/* Value of pointed to integer: 1 */
यदि पॉइंटर किसी संरचना या संघ प्रकार की ओर इशारा करता है तो आप इसे डिरेल कर सकते हैं और इसके सदस्यों तक सीधे पहुंच सकते हैं ->
ऑपरेटर:
SomeStruct *s = &someObject;
s->someMember = 5; /* Equivalent to (*s).someMember = 5 */
सी में, एक पॉइंटर एक अलग मूल्य प्रकार है जिसे फिर से असाइन किया जा सकता है और अन्यथा इसे अपने आप में एक चर के रूप में माना जाता है। उदाहरण के लिए निम्न उदाहरण सूचक (चर) के मान को ही प्रिंट करता है।
printf("Value of the pointer itself: %p\n", (void *)pointer);
/* Value of the pointer itself: 0x7ffcd41b06e4 */
/* This address will be different each time the program is executed */
क्योंकि एक पॉइंटर एक परिवर्तनशील परिवर्तनशील चर है, इसके लिए यह संभव है कि वह किसी वैध वस्तु की ओर इशारा न करे, या तो शून्य पर सेट किया जा सकता है
pointer = 0; /* or alternatively */
pointer = NULL;
या बस एक मनमाना बिट पैटर्न है कि एक वैध पता नहीं है युक्त। उत्तरार्द्ध एक बहुत ही खराब स्थिति है, क्योंकि यह परीक्षण नहीं किया जा सकता है इससे पहले कि सूचक को विघटित किया जा रहा है, केवल एक परीक्षण के लिए एक सूचक शून्य है:
if (!pointer) exit(EXIT_FAILURE);
यदि कोई वैध वस्तु की ओर इशारा करता है, तो एक संकेतक को केवल निलंबित किया जा सकता है, अन्यथा व्यवहार अपरिभाषित है। कई आधुनिक कार्यान्वयन कुछ प्रकार की त्रुटि जैसे कि खराबी दोष को समाप्त करने और निष्पादन को समाप्त करने में आपकी सहायता कर सकते हैं, लेकिन अन्य लोग आपके कार्यक्रम को अमान्य स्थिति में छोड़ सकते हैं।
Dereference संचालक द्वारा लौटाया गया मान मूल चर का एक उत्परिवर्ती उर्फ है, इसलिए मूल चर को संशोधित करते हुए इसे बदला जा सकता है।
*pointer += 1;
printf("Value of pointed to variable after change: %d\n", *pointer);
/* Value of pointed to variable after change: 2 */
पॉइंटर्स भी पुनः असाइन किए जा सकते हैं। इसका मतलब यह है कि किसी ऑब्जेक्ट को इंगित करने वाला पॉइंटर बाद में उसी प्रकार के किसी अन्य ऑब्जेक्ट को इंगित करने के लिए उपयोग किया जा सकता है।
int value2 = 10;
pointer = &value2;
printf("Value from pointer: %d\n", *pointer);
/* Value from pointer: 10 */
किसी भी अन्य चर की तरह, पॉइंटर्स का एक विशिष्ट प्रकार होता है। उदाहरण के लिए, आप एक short int
के पते को एक पॉइंटर को एक long int
को असाइन नहीं कर सकते। इस तरह के व्यवहार को टाइपिंग के रूप में संदर्भित किया जाता है और सी में निषिद्ध है, हालांकि कुछ अपवाद हैं।
हालाँकि पॉइंटर एक विशिष्ट प्रकार का होना चाहिए, लेकिन प्रत्येक प्रकार के पॉइंटर के लिए आवंटित मेमोरी, पते को स्टोर करने के लिए पर्यावरण द्वारा उपयोग की जाने वाली मेमोरी के बराबर होती है, न कि उस प्रकार के आकार की तुलना में जिसे इंगित किया जाता है।
#include <stdio.h>
int main(void) {
printf("Size of int pointer: %zu\n", sizeof (int*)); /* size 4 bytes */
printf("Size of int variable: %zu\n", sizeof (int)); /* size 4 bytes */
printf("Size of char pointer: %zu\n", sizeof (char*)); /* size 4 bytes */
printf("Size of char variable: %zu\n", sizeof (char)); /* size 1 bytes */
printf("Size of short pointer: %zu\n", sizeof (short*)); /* size 4 bytes */
printf("Size of short variable: %zu\n", sizeof (short)); /* size 2 bytes */
return 0;
}
(NB: यदि आप Microsoft विज़ुअल स्टूडियो का उपयोग कर रहे हैं, जो C99 या C11 मानकों का समर्थन नहीं करता है, तो आपको उपरोक्त नमूने में %zu
बजाय %Iu
1 का उपयोग करना होगा।)
ध्यान दें कि उपरोक्त परिणाम पर्यावरण से पर्यावरण की संख्या में भिन्न हो सकते हैं लेकिन सभी वातावरण विभिन्न प्रकार के सूचक के लिए समान आकार दिखाते हैं।
कार्डिफ विश्वविद्यालय सी पॉइंटर्स परिचय से जानकारी के आधार पर निकालें
संकेत और सारणी
सी में अंक और सरणियाँ अंतरंग रूप से जुड़े हुए हैं। C में Arrays हमेशा स्मृति में सन्निहित स्थानों में आयोजित की जाती हैं। पॉइंटर अंकगणित को हमेशा इंगित किए गए आइटम के आकार द्वारा बढ़ाया जाता है। इसलिए यदि हमारे पास तीन डबल्स की एक सरणी है, और आधार के लिए एक सूचक है, तो *ptr
पहले डबल को संदर्भित करता है, *(ptr + 1)
दूसरे को, *(ptr + 2)
तीसरे को। सरणी संकेतन []
का उपयोग करने के लिए एक अधिक सुविधाजनक संकेतन है।
double point[3] = {0.0, 1.0, 2.0};
double *ptr = point;
/* prints x 0.0, y 1.0 z 2.0 */
printf("x %f y %f z %f\n", ptr[0], ptr[1], ptr[2]);
तो अनिवार्य रूप से ptr और सरणी नाम विनिमेय हैं। इस नियम का अर्थ यह भी है कि एक सरणी एक सबरूटीन के पास जाने पर एक पॉइंटर की ओर घटती है।
double point[3] = {0.0, 1.0, 2.0};
printf("length of point is %s\n", length(point));
/* get the distance of a 3D point from the origin */
double length(double *pt)
{
return sqrt(pt[0] * pt[0] + pt[1] * pt[1] + pt[2] * pt[2])
}
एक सूचक किसी भी तत्व को किसी सरणी में, या अंतिम तत्व से परे तत्व को इंगित कर सकता है। हालाँकि यह सरणी के पहले तत्व सहित किसी अन्य मान के लिए सूचक सेट करने के लिए एक त्रुटि है। (कारण यह है कि खंडित आर्किटेक्चर पर पहला तत्व खंड सीमा को पार कर सकता है इससे पहले पता, संकलनकर्ता सुनिश्चित करता है कि अंतिम तत्व प्लस एक के लिए नहीं होता है)।
फुटनोट 1: Microsoft प्रारूप जानकारी को printf()
और प्रारूप विनिर्देश सिंटैक्स के माध्यम से पाया जा सकता है।
शून्य बिंदुओं के साथ बहुरूपी व्यवहार
qsort()
मानक लाइब्रेरी फ़ंक्शन इस बात का एक अच्छा उदाहरण है कि कैसे एक एकल फ़ंक्शन को विभिन्न प्रकार के विभिन्न प्रकारों पर संचालित करने के लिए शून्य पॉइंटर्स का उपयोग किया जा सकता है।
void qsort (
void *base, /* Array to be sorted */
size_t num, /* Number of elements in array */
size_t size, /* Size in bytes of each element */
int (*compar)(const void *, const void *)); /* Comparison function for two elements */
छांटे जाने वाले सरणी को एक शून्य सूचक के रूप में पारित किया जाता है, इसलिए किसी भी प्रकार के तत्व की एक सरणी को संचालित किया जा सकता है। अगले दो तर्क qsort()
को बताते हैं कि सरणी में कितने तत्वों की अपेक्षा करनी चाहिए, और बाइट्स में कितना बड़ा है, प्रत्येक तत्व है।
अंतिम तर्क एक तुलनात्मक फ़ंक्शन के लिए एक फ़ंक्शन पॉइंटर है जो स्वयं दो शून्य पॉइंटर्स लेता है। कॉलर को यह फ़ंक्शन प्रदान करने से, qsort()
प्रभावी रूप से किसी भी प्रकार के तत्वों को सॉर्ट कर सकता है।
फ़्लोट्स की तुलना करने के लिए इस तरह के एक तुलनात्मक फ़ंक्शन का एक उदाहरण दिया गया है। ध्यान दें कि qsort()
को qsort()
गए किसी भी तुलनात्मक फ़ंक्शन को इस प्रकार के हस्ताक्षर की आवश्यकता है। जिस तरह से इसे पॉलीमॉर्फ़िक बनाया गया है वह शून्य पॉइंटर तर्कों को इंगित करने के लिए है जिस तरह के तत्व की हम तुलना करना चाहते हैं।
int compare_floats(const void *a, const void *b)
{
float fa = *((float *)a);
float fb = *((float *)b);
if (fa < fb)
return -1;
if (fa > fb)
return 1;
return 0;
}
चूँकि हम जानते हैं कि qtsort इस फ़ंक्शन का उपयोग फ़्लोट्स की तुलना करने के लिए करेगा, हम शून्य सूचक तर्कों को डेरेफ़र करने से पहले फ्लोट पॉइंटर्स पर वापस लाते हैं।
अब, लंबाई "लेन" के साथ एक सरणी "सरणी" पर बहुरूपी समारोह qsort का उपयोग बहुत सरल है:
qsort(array, len, sizeof(array[0]), compare_floats);