Theory
हर तरह के घर के लिए एक पर्ची
पिछले lesson में आपने अपना पहला pointer लिखा। आज drill: marks program में एक int marks, एक float percentage और एक char grade है, और हर एक को अपना pointer चाहिए।
पहली सहज सोच: "address तो address है, एक ही pointer type सबके लिए चलना चाहिए।"
C असहमत है, अच्छी वजह से: जब courier address पर पहुँचे, उसे पता होना चाहिए कि 4 bytes उठाने हैं या 1, और उन्हें कैसे पढ़ना है। पर्ची एक TYPE साथ रखती है।
Theory
हर type का एक pointer
int marks = 78; int *pi = &marks;
float perc = 78.5; float *pf = &perc;
char grade = 'A'; char *pc = &grade;
नियम: pointer का type उससे मेल खाता है जिसे वह point करता है। क्यों? Dereference को box का size और encoding पता होना चाहिए: *pi 4 bytes को int के रूप में पढ़ता है, *pc 1 byte को char के रूप में। एक float को किसी int पर point कीजिए और compiler ऐतराज़ करता है, और अगर ज़बरदस्ती किया, तो pf उस int के bytes को float बकवास के रूप में पढ़ेगा, Unit 1 का costume-mismatch वाला garbage, अब memory के स्तर पर।
Theory
पर्ची पर parcel का size
किसी godown से pickup की एक address slip सिर्फ यह नहीं बताती कि कहाँ, यह भी बताती है कि क्या लेना है: "4-byte int का crate" या "1-byte char का envelope"। सही address पर गलत slip वाला courier गलत मात्रा उठा लेता है और माल बिगाड़ देता है। और एक खाली slip? वह है NULL: एक ईमानदार "अभी कोई मंज़िल नहीं", किसी बेतरतीब लिखे address वाली slip (एक uninitialized, wild pointer) से अनगिनत गुना सुरक्षित।
Practical
पूरा pointer drill
#include <stdio.h>
int main() {
int marks = 78;
float perc = 78.5;
char grade = 'A';
int *pi = &marks;
float *pf = &perc;
char *pc = &grade;
int *safe = NULL; /* points nowhere, on purpose */
printf("%d %.1f %c\n", *pi, *pf, *pc);
if (safe != NULL) printf("never runs\n");
return 0;
}This example runs in Gri-Learn on the web, where you can edit it and see the output.
Quiz
किसी float के pointer को int* के बजाय float* क्यों declare करना ज़रूरी है?
- Dereferencing type से जानता है कि कितने bytes पढ़ने हैं और उन्हें कैसे interpret करना है
- यह सिर्फ एक naming convention है; कोई भी pointer type एक जैसा काम करता है
- float* pointers memory के एक ख़ास हिस्से में store होते हैं
- int* pointers बड़े addresses नहीं रख सकते
Show the answer
Dereferencing type से जानता है कि कितने bytes पढ़ने हैं और उन्हें कैसे interpret करना है
सभी pointers एक जैसे दिखने वाले addresses रखते हैं; TYPE dereference के लिए निर्देश है: pf 4 bytes को IEEE float के रूप में पढ़ता है, pc 1 byte को एक character के रूप में। गलत type = गलत width + गलत interpretation = garbage। "pointer types क्यों ज़रूरी हैं?" एक स्थायी exam short-answer है, और यही उसका पूरे-marks वाला जवाब है।
Theory
NULL और typeless void*
NULL: जान-बूझकर कहीं-नहीं। int *p = NULL; कहता है "अभी कोई target नहीं", और code इसे CHECK कर सकता है: if (p != NULL)। इसके उलट wild pointer: declare किया, कभी initialize नहीं, एक बेतरतीब address रखता है, उसे dereference कीजिए और program corrupt होता है या crash। NULL checkable है; wild अफ़रा-तफ़री है।
*void \vp: generic पर्ची, किसी भी type का address लेती है (`vp = &marks; vp = &perc;`)। कीमत: इसे सीधे dereference नहीं किया जा सकता*, C को parcel का size नहीं पता। पहले cast कीजिए: `(int )vp`। Libraries एक ही function से हर type की सेवा करने के लिए void इस्तेमाल करती हैं।
Think first
ख़तरा पहचानिए
तीन pointers: int a = &marks; int b = NULL; int *c; (कभी assign नहीं हुआ)। इन्हें rank कीजिए: कौन सा dereference सुरक्षित है, कौन सा एक पक्का crash है जिसे आप कम-से-कम TEST कर सकते हैं, और कौन सा सबसे बुरा है?
Show the answer
*a सुरक्षित है: यह एक असली box पर target करता है। *b crash करता है, पर अनुमान से, और code इसकी रखवाली कर सकता है: if (b != NULL)। *c सबसे बुरा है: एक wild pointer जो बेतरतीब garbage रखता है; इसे dereference करने पर data चुपचाप corrupt हो सकता है और demo वाले दिन तक "काम" करता रहे। हर C programmer की दीवार पर छपा सबक: pointers को initialize कीजिए, किसी target पर या NULL पर, उन्हें कभी wild मत छोड़िए।
Quiz
void *vp = &marks; vp के ज़रिए int पढ़ने से पहले क्या ज़रूरी है?
- एक cast: *(int *)vp, क्योंकि void* कोई size जानकारी नहीं रखता
- कुछ नहीं: *vp सीधे काम करता है
- vp को पहले NULL set करना होगा
- void pointers कभी इस्तेमाल लायक नहीं बनाए जा सकते
Show the answer
एक cast: *(int *)vp, क्योंकि void* कोई size जानकारी नहीं रखता
void WHERE store करता है पर WHAT नहीं: कोई type नहीं, कोई byte-width नहीं, इसलिए सादा vp गैर-कानूनी है। Cast (int )vp address को कुछ देर के लिए उसके असली type में पहना देता है, और तब dereference जानता है कि 4 bytes को एक int के रूप में पढ़ना है। "Generic पर cast-before-use" void की पूरी exam कहानी है।
Watch out
Marks कहाँ कटते हैं
Type mismatches: float pf = &marks कुछ setups पर warnings के साथ compile होता है और बुरा बर्ताव करता है; types हमेशा मिलाइए। Wild pointers: int p; p = 5; corruption की एक lottery है, किसी target पर या NULL पर initialize कीजिए। बिना cast void\ को dereference करना*: compile error, और WHY जानना (कोई size जानकारी नहीं) marks दिलाता है। Style note: int p और int p दोनों compile होते हैं; NAME से जुड़ता है (int* a, b एक pointer और एक int declare करता है, एक शातिर classic)।
Theory
आगे: pointers चलना सीखते हैं
अगले lesson की एक झलक: चूँकि pi एक address रखता है, तो pi + 1 क्या है? Address-जमा-एक-byte नहीं, C ज़्यादा समझदार है: यह एक पूरे box से आगे बढ़ता है (int के लिए 4 bytes, char के लिए 1), और यही pointers और arrays को पक्का दोस्त बनाता है। Pointer arithmetic, आगे।
Summary
Key takeaways
- हर type के हिसाब से declare कीजिए: int pi, float pf, char *pc; type को target से मेल खाना चाहिए।
- Type, dereference को बताता है कि कितने bytes पढ़ने हैं और उन्हें कैसे interpret करना है।
- NULL = जान-बूझकर कहीं-नहीं, if (p != NULL) से checkable; wild pointers = बेतरतीब अफ़रा-तफ़री।
- हर pointer initialize कीजिए: किसी असली target पर या NULL पर, उसे कभी wild मत छोड़िए।
- void किसी भी address को रखता है पर dereference से पहले cast करना ज़रूरी है: (int *)vp।
- याद रखने का hook: पर्ची parcel का size बताती है; खाली पर्ची, बेतरतीब लिखी पर्ची से बेहतर।