5.1.22 · D3 · Coding › C Programming › Structures — declaration, accessing members (. and - - )
Intuition Yeh page kis liye hai
Parent note ne tumhe bataya tha ki . aur -> ka kya matlab hai. Yeh page har us situation ko dhundhta hai jisme struct member-access tumhe daal sakta hai — plain value, pointer, nested, array-of-structs, pointer-to-struct-inside-array, ek struct field jo khud ek pointer hai, degenerate (empty/zero/null) cases, aur exam traps — aur har ek ko ground up se solve karta hai. End tak tumhe koi bhi access pattern nahi milna chahiye jise tumne pehle solve hote nahi dekha ho.
Shuru karne se pehle: do words jinpar hum baar baar rely karenge.
Definition "Object" vs "address" — ek hi fork jo matter karti hai
Tum object hold karte ho jab ek variable khud struct hai (e.g. struct Point a;). Member lena ho to: ==dot .== use karo.
Tum address hold karte ho jab ek variable store karta hai ki struct kahan rehti hai — ek pointer (e.g. struct Point *p;). Member lena ho to: ==arrow ->== use karo, kyunki pehle tumhe object tak travel karna padta hai.
Neeche ka har example bas yahi ek fork hai, baar baar apply ki gayi.
Neeche un saare case-classes ki poori list hai jisme ek member access pad sakta hai. Baad ke har example par un cell(s) ka tag hai jo wo cover karta hai, taaki tum grid fill hote dekh sako.
#
Case class
Isme kya tricky hai
Covered by
A
Plain value, single-level
baseline — sirf .
Ex 1
B
Pointer to value, single-level
. nahi, -> use karna padega
Ex 2
C
. aur -> ek hi result dete hain
prove karna ki p->m == (*p).m
Ex 3
D
Nested struct, all values
dots ki chain e.when.d
Ex 4
E
Nested struct via a pointer
pehla hop ->, phir .
Ex 4
F
Array of structs (value access)
pehle index, phir dot
Ex 5
G
Pointer walking an array of structs
-> jab pointer move karta hai
Ex 6
H
Function caller ke struct ko modify kare
kyun &a + andar ->
Ex 7
I
Degenerate: zero-initialised / "empty" struct
struct value 0 par kaisi dikhti hai
Ex 8
J
Real-world word problem
ek kahani ko structs mein translate karo
Ex 9
K
Exam twist: *p.m vs (*p).m precedence trap
operator binding
Ex 10
L
Field jo khud ek pointer-to-struct hai
. phir -> chained
Ex 11
M
Degenerate: -> null / uninitialised pointer ke through
undefined behaviour / crash
Ex 12
Worked example Example 1 — plain value, single level
(cell A)
struct Point { int x, y; };
struct Point a;
a.x = 3 ;
a.y = 4 ;
printf ( "( %d , %d )" , a.x, a.y);
Forecast: aage padhne se pehle printed line guess karo.
a ek struct value ke roop mein declare hua hai — object khud a mein rehta hai.
Yeh step kyun? Humein jaanna hai ki hum fork ke kis side par hain. a ek object hai → dot.
a.x = 3 x slot mein likhta hai; a.y = 4 alag y slot mein likhta hai.
Yeh step kyun? Members alag-alag boxes hain — ek ko touch karne se doosra kabhi affect nahi hota.
printf dono ko . se wapas padhta hai.
Yeh step kyun? Wahi fork: ab bhi object hold kar rahe hain.
Verify: x=3, y=4 → output hai (3,4). Alag slots, koi interference nahi.
Neeche ki figure exactly yahi draw karti hai: object a outer red box hai, aur x aur y do alag inner black slots hain. Notice karo ki dono slots kabhi overlap nahi karte — isliye a.x set karne se a.y akela rehta hai. Har arrow ko slot se neeche label a.x / a.y tak trace karo jo tumhe wahan pahunchne ke liye likhna padega.
Worked example Example 2 — pointer to value
(cell B)
struct Point a = { 3 , 4 };
struct Point * p = & a; // p stores the ADDRESS of a
p -> x = 10 ;
printf ( "( %d , %d )" , a.x, a.y);
Forecast: kya p->x badalne se a change hoga? Kya print hoga?
&a a ka address produce karta hai; p use store karta hai.
Yeh step kyun? Ab hum address hold kar rahe hain, object nahi → fork humhe -> ki taraf bhejta hai.
p->x = 10 ka matlab hai "us object tak jao jise p point karta hai, phir uska x set karo". Woh object hai hi a.
Yeh step kyun? Yahan sirf ek Point hai. p koi copy nahi banata — yeh a ke same bytes ko point karta hai.
a.x (ab changed) value padhta hai.
Yeh step kyun? Confirm karta hai ki write real object tak pahunchi.
Verify: a.x 10 ho gaya, a.y untouched → output (10,4).
Figure mein, baayein taraf ka red box p hai — usme Point nahi, &a hai. Red arrow woh "travel" step hai jo p->x koi bhi member touch karne se pehle perform karta hai; use across follow karo real object a tak jo daayein taraf hai, jahan x 10 ho gaya hai.
p.x try karna
p ek address hai, struct nahi. p.x deta hai "request for member 'x' in something not a structure" . Fork rule: address → arrow.
Worked example Example 3 — prove karna ki
p->m equals (*p).m (cell C)
struct Point a = { 3 , 4 };
struct Point * p = & a;
int viaArrow = p -> y;
int viaStar = ( * p).y;
printf ( " %d %d " , viaArrow, viaStar);
Forecast: kya dono numbers same hain? Kyun hone hi chahiye?
*p dereference karta hai — yeh hai hi object a.
Yeh step kyun? Address follow karne par object wapas milta hai.
(*p).y phir us object mein dot karta hai. Parentheses force karte hain "pehle dereference karo".
Yeh step kyun? . * se zyada tightly bind karta hai, isliye bina parens ke compiler *(p.y) padhta hai — jo nonsense hai.
p->y language ke according exactly step 2 hi mean karta hai, bas zyada sundar.
Yeh step kyun? Dikhata hai ki arrow pure shorthand hai, koi naya behaviour nahi.
Verify: dono a.y = 4 padhte hain → output 4 4. Equal, guaranteed.
Worked example Example 4 — nested struct, by value aur via pointer
(cells D, E)
struct Date { int d, m, y; };
struct Event { char title [ 10 ]; struct Date when; };
struct Event e;
strcpy (e.title, "Exam" );
e.when.d = 25 ; // (cell D)
e.when.m = 12 ;
struct Event * pe = & e;
pe -> when.y = 2025 ; // (cell E)
printf ( " %s %d - %d - %d " , e.title, e.when.d, e.when.m, e.when.y);
Forecast: kaun si date print hogi?
e ek value hai → e.when bhi ek value hai (ek inner Date object jo e ke andar rehta hai).
Yeh step kyun? Nesting fork ko change nahi karta; har . ek aur object par land karta hai.
e.when.d = 25: pehla dot inner Date tak pahunchta hai, doosra dot uske field d tak pahunchta hai.
Yeh step kyun? Woh operator chain karo jo abhi tumhare paas jo hai use match kare. Dono hops values hain → dono dots.
pe ek pointer hai → pehla hop -> se e tak pahunchta hai. Lekin pe->when phir se ek value hai (inner Date), isliye hum . ke saath continue karte hain.
Yeh step kyun? Fork ko har hop par re-evaluate karo. Pointer → ek baar arrow; inner struct value se held hai → baad mein dot.
Verify: title Exam, date 25-12-2025 → output Exam 25-12-2025.
Har dot par fork dobara pucho. "Kya main abhi ek object hold kar raha hoon ya ek address?" Jawab per-hop do, poori line ke liye ek baar nahi.
Worked example Example 5 — array of structs
(cell F)
struct Point pts [ 3 ] = { { 1 , 1 }, { 2 , 4 }, { 3 , 9 } };
int sum = 0 ;
for ( int i = 0 ; i < 3 ; i ++ )
sum += pts [i].y; // pehle index karo, phir dot
printf ( " %d " , sum);
Forecast: y values ka sum guess karo.
pts[i] array mein i-wa struct value select karta hai.
Yeh step kyun? Structs ki array ko index karne par ek struct object milta hai (pointer nahi). Object → dot.
pts[i].y us object ka y padhta hai.
Yeh step kyun? [] . se zyada tightly bind karta hai, isliye pts[i].y sahi se (pts[i]).y mean karta hai — koi parens zaroori nahi.
1 + 4 + 9 accumulate karo.
Yeh step kyun? Verify ke liye sanity target.
Verify: 1 + 4 + 9 = 14 → output 14.
Figure teen struct values ko side by side rakhti hai. Har box mein red y field woh hai jo loop pull out karta hai; har box ke neeche red arrow ko exact access expression pts[i].y tak follow karo. Upar red mein running total woh hai jo sum accumulate karta hai.
Worked example Example 6 — pointer se walk karna
(cell G)
struct Point pts [ 3 ] = { { 1 , 1 }, { 2 , 4 }, { 3 , 9 } };
struct Point * q = pts; // q points at pts[0]
int sum = 0 ;
for ( int i = 0 ; i < 3 ; i ++ ) {
sum += q->x; // q is a pointer → arrow
q ++ ; // next struct par step karo
}
printf ( " %d " , sum);
Forecast: x values ka sum kya aayega?
q = pts q mein pehle element ka address store karta hai.
Yeh step kyun? Ek array name apne pehle element ke pointer mein decay ho jaata hai. To ab hum address hold kar rahe hain → arrow.
q->x us struct ka x padhta hai jise q abhi point kar raha hai.
Yeh step kyun? Fork: address → ->.
q++ pointer ko ek poore struct se advance karta hai (ek byte se nahi). Compiler step ko sizeof(struct Point) se scale karta hai.
Yeh step kyun? Yeh woh crucial pointer-arithmetic rule hai jo loop ko exactly ek baar har element visit karwaata hai.
Verify: 1 + 2 + 3 = 6 → output 6.
Figure q ko ek red marker ke roop mein dikhati hai jo array ke upar hop karta hai. Har red q++ arrow ek poore struct ki jump hai — kabhi single byte nahi — isliye marker har baar cleanly agle box ke start par land karta hai.
q++ kyun sizeof se jump karta hai aur 1 se nahi, iske liye dekho Pointers in C .
Worked example Example 7 — function caller ke struct ko modify karti hai
(cell H)
void grow ( struct Point * r ) {
r->x += 1 ;
r->y += 1 ;
}
struct Point a = { 3 , 4 };
grow ( & a );
printf ( "( %d , %d )" , a.x, a.y);
Forecast: kya a change hoga? Kya bnega?
grow(&a) a ka address pass karta hai, uski copy nahi.
Yeh step kyun? Agar hum a by value pass karte, to grow ek throw-away copy edit karta aur main ka a (3,4) rehta.
Andar, r woh address hold karta hai → real object tak pahunchne ke liye -> use karo.
Yeh step kyun? Fork: address → arrow. r->x += 1 same bytes edit karta hai jahan a rehti hai.
main mein wapas, a change reflect karta hai.
Yeh step kyun? Confirm karta hai ki edit function boundary cross kar gayi.
Verify: a becomes (4,5) → output (4,5).
By-value vs by-pointer trade-offs ke liye dekho Passing Structures to Functions .
Worked example Example 8 — zero-initialised (empty) struct
(cell I)
struct Student { char name [ 20 ]; int roll; float marks; };
struct Student blank = { 0 }; // har member zeroed
printf ( "[ %s ] %d %.1f " , blank.name, blank.roll, blank.marks);
Forecast: ek "empty" struct kya print karega? Kya name crash karega?
= {0} pehle member ko 0 initialize karta hai aur, C rule ke anusaar, baaki sab ko bhi zero kar deta hai.
Yeh step kyun? Yeh "blank" struct paane ka safe, defined tarika hai — koi garbage nahi.
name ek array ban jaata hai jiska pehla byte '\0' (0) hai. %s se print karne par, yeh empty string hai — safe, crash nahi.
Yeh step kyun? Zero pehla byte ek valid, empty C string hai; %s turant ruk jaata hai.
roll 0 hai, marks 0.0 hai.
Yeh step kyun? Confirm karta hai ki har slot ek defined zero hai.
Verify: output [] 0 0.0. [] ek empty (garbage nahi) name dikhata hai.
uninitialised chhodna
struct Student x; (koi = {0} nahi) local variable ke roop mein garbage contain karta hai, zeros nahi. x.name ko %s se print karna crash kar sakta hai. Hamesha initialize karo, ya memset(&x, 0, sizeof x) karo.
Worked example Example 9 — real-world: coins ki wallet
(cell J)
Story: Ek wallet mein ₹1, ₹5 aur ₹10 coins ki count hai. Ise ek struct se model karo aur total money compute karo.
struct Wallet { int ones, fives, tens; };
struct Wallet w = { 7 , 3 , 2 }; // 7×1, 3×5, 2×10
int total = w.ones * 1 + w.fives * 5 + w.tens * 10 ;
printf ( "Rs %d " , total);
Forecast: compute karne se pehle rupee total guess karo.
Entity identify karo (ek wallet) aur uske fields (har coin ki counts) → ek struct, teen int members.
Yeh step kyun? Structs tab shine karte hain jab kai values ek hi cheez describe karein; wallet exactly yahi hai.
w ek value hai → har field . se padhi jaati hai.
Yeh step kyun? Fork: hum object hold kar rahe hain.
Har count ko uski coin value se weight karo aur sum karo: 7 ⋅ 1 + 3 ⋅ 5 + 2 ⋅ 10 .
Yeh step kyun? "Total money" word ko members par arithmetic mein convert karta hai.
Verify: 7 + 15 + 20 = 42 → output Rs 42. Units: coins × rupees/coin = rupees. ✓
Worked example Example 10 —
*p.m vs (*p).m (cell K)
struct Box { int * ptr; };
int value = 99 ;
struct Box b = { & value };
struct Box * p = & b;
// Trick: *p.ptr kya karne ki koshish karta hai?
printf ( " %d " , * ( * p).ptr); // SAHI tarika
Forecast: kyun *p.ptr (bina inner parens ke) compile nahi hota, aur sahi line kya print karti hai?
. * se zyada tightly bind karta hai. Isliye compiler *p.ptr ko *(p.ptr) padhta hai.
Yeh step kyun? Precedence meaning se pehle grouping decide karta hai. Yahi poora trap hai.
Lekin p ek pointer hai, isliye p.ptr illegal hai (p struct nahi hai) → compile error. Tumhe p pehle dereference karne ke liye (*p).ptr likhna padega. Equivalently p->ptr.
Yeh step kyun? Fork rule phir se: address → pehle travel karo.
(*p).ptr hai b.ptr, jo hai &value. Bahar ka * phir us through padhta hai value tak pahunchne ke liye.
Yeh step kyun? Indirection ke do levels: ek box tak pahunchne ke liye, ek us int tak pahunchne ke liye jise box point karta hai.
Verify: (*p).ptr = &value, *(&value) = 99 → output 99.
Worked example Example 11 — dot phir arrow, chained
(cell L)
struct Date { int d, m, y; };
struct Container { struct Date * when; }; // field ek Date ka POINTER hai
struct Date today = { 25 , 12 , 2025 };
struct Container c;
c.when = & today; // field ek ADDRESS store karta hai
printf ( " %d " , c.when -> d ); // field tak dot, PHIR us through arrow
Forecast: kaun sa number print hoga, aur line ke beech mein operator kyun change hota hai?
c ek value hai → uske field tak . se pahuncho: c.when.
Yeh step kyun? Fork, pehla hop: hum object c hold kar rahe hain, isliye dot.
Lekin c.when ek Date nahi hai — yeh ek Date ka pointer hai (ek address). To fork flip ho jaati hai: uske member tak pahunchne ke liye ab humein -> chahiye.
Yeh step kyun? Har hop ke baad fork dobara pucho. Pehle hop ne humein address diya, isliye agla hop -> se travel karta hai.
c.when->d = "c ka field when, phir us pointer ke through today ke andar d tak travel karo".
Yeh step kyun? Yeh Example 4 ka mirror image hai: wahan inner struct ek value thi (arrow ke baad dot); yahan yeh ek pointer hai (dot ke baad arrow).
Verify: c.when today ko point karta hai, jiska d 25 hai → output 25.
Figure mein flip action mein dikhti hai: c ek plain box hai (. se reach hota hai), lekin uska when slot ek red arrow (ek address) hold karta hai jise tumhe -> se follow karna padta hai separate today object par land karne ke liye.
Mnemonic Dot-then-arrow vs arrow-then-dot
Operator sirf is par depend karta hai ki field khud kya hai : nested struct value → dotting jaari rakho; nested struct pointer → -> par switch karo. Line ke start se kabhi guess mat karo — har hop inspect karo.
Worked example Example 12 — arrow through a null / uninitialised pointer
(cell M)
struct Date { int d, m, y; };
struct Date * p = NULL ; // kahin point nahi karta
int day = p -> d; // UNDEFINED BEHAVIOUR — likely crash
struct Date * q; // uninitialised: GARBAGE address hold karta hai
int bad = q -> d; // UNDEFINED BEHAVIOUR — ek random address padhta hai
Forecast: fork rule kehta hai "address → arrow", isliye p->d legal lagta hai. Phir bhi yeh catastrophic kyun hai?
-> hamesha pehle dereference karta hai (Example 3: p->d ≡ (*p).d). Dereference karne ka matlab hai "is address par bytes padho".
Yeh step kyun? Operator ka matlab fixed hai; yeh kabhi nahi check karta ki address valid hai ya nahi.
NULL "kahin nahi" ka address hai (numerically 0). "Kahin nahi" par object padhna allowed nahi hai — OS usually program ko segmentation fault se kill kar deta hai.
Yeh step kyun? Fork rule pass karna ("address hold karo → arrow") zaroori hai lekin sufficient nahi : address ek real object par bhi point karna chahiye.
q kabhi assign nahi hua, isliye yeh ek leftover garbage address hold karta hai. q->d koi random memory padhta hai — kabhi kabhi "kaam karta hai", kabhi data corrupt karta hai, kabhi crash karta hai. Woh unpredictability hi undefined behaviour hai.
Yeh step kyun? Dikhata hai ki "is baar crash nahi hua" ka matlab code sahi hai aisa nahi.
Verify (mental, value nahi): check karne ke liye koi defined answer nahi hai — program ka behaviour undefined hai. Sahi fix hai pehle guard karo:
if (p != NULL ) day = p -> d; // sirf tab travel karo jab address real ho
Common mistake "Compile hua aur ek baar run hua, to theek hai"
NULL ya uninitialised pointer par -> cleanly compile hota hai aur lagta bhi hai ki kaam karta hai. Hamesha ensure karo ki ek struct pointer ek real object par set hai (via &obj, successful malloc, ya array element) use follow karne se pehle. Dekho Pointers in C aur Linked Lists — list traverse karna matlab har node->next par node != NULL check karna.
Recall Quick self-test
struct Node *n; diya, member data kaise padhoge? ::: n->data (n ek pointer hai → arrow)
struct Node arr[5]; diya, element 2 ka data kaise padhoge? ::: arr[2].data (indexing ek value deta hai → dot)
struct Point *q par q++ ek 1-byte step kyun NAHI hai? ::: Yeh sizeof(struct Point) se scale karta hai isliye agla poora struct milta hai.
= {0} ek struct ke liye kya guarantee karta hai? ::: Har member zero-initialised hai (empty string, 0, 0.0).
Agar c.when ek struct Date * hai, to uska d kaise padhoge? ::: c.when->d — field tak dot, phir pointer ke through arrow.
p jab NULL ho to p->d catastrophic kyun hai? ::: -> pehle dereference karta hai; "kahin nahi" address par object padhna undefined behaviour hai (usually crash).
Pointers in C — har fork ka address side, q++ scaling, aur null/uninitialised pointer dangers.
Arrays vs Structures — Ex 5 & 6 dono mix karte hain: ek array of structs .
Linked Lists — Ex 11 & 12 ka pointer-inside-struct ek node ka seed hai; traversal null ke against guard karta hai.
typedef — in type names se struct hatao.
Memory Alignment & Padding — kyun q++ padding bytes bhi skip kar sakta hai.
Passing Structures to Functions — Ex 7 poori tarah se.
Parent: 5.1.22 Structures — declaration, accessing members (. and - - ) (Hinglish)
function edits caller via arrow
check not null before travel