5.1.27 · D3 · Coding › C Programming › Preprocessor directives — #define, #ifdef, #ifndef, #include
Intuition Yeh page kya hai
Parent note ne tumhe #define, #ifdef, #ifndef, aur include guards ke rules sikhaye the. Yahan hum un rules ko har tarah ke case ke against stress-test karte hain jo yeh topic tumhare saamne rakh sakta hai — har macro-expansion trap, har conditional branch, degenerate aur limiting inputs, ek real-world word problem, aur ek exam-style twist. Har ek ke liye, tum pehle output forecast karo (dekhne se pehle guess karo), phir hum text ko haath se bilkul waise expand karte hain jaise preprocessor karta hai, phir hum verify karte hain.
the parent topic ka golden rule yaad karo: preprocessor blind text substitution aur text deletion karta hai. Yeh kabhi compute nahi karta, kabhi types check nahi karta. Jab hum yahan koi number compute karte hain, toh woh compiler hai jo already-substituted text par arithmetic kar raha hai.
Is topic ke har problem ka jawab inhi case classes mein se ek mein aata hai. Neeche ke worked examples mein har ek ko us cell ke saath tag kiya gaya hai jise woh cover karta hai, aur milkar woh har cell ko cover karte hain.
#
Case class
Kya galat ho sakta hai / kya dhyan dena hai
Example jo ise cover karta hai
A
Object-like macro, simple constant
text swap, preprocessor dwara koi arithmetic nahi
Ex 1
B
Function-like macro, precedence danger
missing parens grouping badal dete hain
Ex 2
C
Function-like macro, side-effect / double-eval
argument do baar evaluate hota hai
Ex 3
D
#ifdef / #ifndef branch selection (defined)
kaun sa text bachta hai
Ex 4
E
#ifndef default value override
external -D built-in default se jeet jaata hai
Ex 5
F
Include guard — paste-twice degenerate case
doosra paste poora delete ho jaata hai
Ex 6
G
Degenerate / zero input ek macro ko
empty arg, 0, macro-defined-as-nothing
Ex 7
H
Real-world word problem (cross-platform)
real target par sahi branch chunna
Ex 8
I
Exam twist — trailing semicolon + nesting
classic arr[MAX;] aur stacked conditions
Ex 9
J
Limiting case — macro jo doosre macro mein expand hoti hai (chained)
rescanning, recursion limits
Ex 10
Worked example Ex 1 — Case A: object-like constant
#define RATE 5
int cost = RATE * 8 + 2 ;
cost mein kaun sa integer hoga?
Forecast: aage padhne se pehle number guess karo. ______
Token RATE ko substitute karo. Preprocessor scan karta hai, RATE milta hai, 5 paste karta hai.
→ int cost = 5 * 8 + 2;
Yeh step kyun? Preprocessor bas yahi karta hai — abhi koi math nahi. Yeh plain-C line compiler ko de deta hai.
Ab compiler compute karta hai. 5 * 8 = 40, phir 40 + 2 = 42.
Yeh step kyun? Multiplication addition se zyada tightly bind karta hai, isliye * pehle hota hai.
Verify: 5 × 8 + 2 = 42 . Macro ne koi arithmetic contribute nahi ki; usne sirf text supply kiya. ✅
Worked example Ex 2 — Case B: function-like macro mein precedence trap
#define AREA ( w , h ) w * h
int a = AREA ( 2 + 3 , 4 );
a kya hai?
Forecast: naam suggest karta hai ( 2 + 3 ) × 4 = 20 . Kya sach mein?
Blindly substitute karo. w ki jagah text 2 + 3 aur h ki jagah 4 paste karo:
→ int a = 2 + 3 * 4;
Yeh step kyun? Substitution pure paste hai — tumhare arguments ke around koi invisible parentheses nahi add hote.
Compiler real precedence ke saath evaluate karta hai. 3 * 4 = 12, phir 2 + 12 = 14.
Yeh step kyun? * ka rank + se zyada hai, isliye intended "pehle add karo" kabhi hota hi nahi.
Verify: hume 14 mila, 20 nahi . Fix hai #define AREA(w, h) ((w) * (h)), jo ((2 + 3) * (4)) = 20 mein expand hota hai. ✅ (Dono values neeche check ki gayi hain.)
Worked example Ex 3 — Case C: double-evaluation / side effects
#define MAX2 ( a , b ) ((a) > (b) ? (a) : (b))
int i = 5 ;
int m = MAX2 (i ++ , 3 );
Yeh run hone ke baad, m aur i kya hain?
Forecast: tum expect kar sakte ho m = 5, i = 6. i++ ko dhyan se dekho.
Macro expand karo. a hai i++, b hai 3:
→ int m = ((i++) > (3) ? (i++) : (3));
Yeh step kyun? Notice karo ki a body mein do baar appear karta hai, isliye i++ do baar paste hua.
Execution trace karo. Pehla (i++) compare karta hai 5 > 3 → true, aur i ko 6 kar deta hai. Kyunki condition true hai, middle branch (i++) run hota hai: yeh 6 yield karta hai, phir i ko 7 kar deta hai.
Yeh step kyun? ? : true-branch choose karta hai, jo argument ko re-evaluate karta hai — ek real function i++ ko exactly ek baar evaluate karta.
Verify: m = 6, i = 7. Yahi woh danger hai jiske baare mein Macros vs inline functions warn karta hai: ek inline function har argument ko ek baar evaluate karta hai; ek macro use utni baar paste karta hai jitni baar woh appear karta hai. ✅
Worked example Ex 4 — Case D:
#ifdef / #ifndef branch selection
#define LOG_LEVEL
#ifdef LOG_LEVEL
int level = 2 ;
#else
int level = 0 ;
#endif
#ifndef LOG_LEVEL
int hidden = 99 ;
#endif
Compiler tak kaun si lines pahunchti hain, aur level kya hai?
Forecast: kya LOG_LEVEL defined hai? Usse har block kya karta hai?
Pehla block: #ifdef LOG_LEVEL. LOG_LEVEL defined hai, isliye #ifdef true hai. int level = 2; rakho aur #else line delete karo.
Yeh step kyun? #ifdef apna body tab rakhta hai jab naam defined ho.
Doosra block: #ifndef LOG_LEVEL. Naam defined hai, isliye #ifndef false hai. Line int hidden = 99; text ke roop mein delete ho jaati hai — variable hidden exist hi nahi karta.
Yeh step kyun? #ifndef #ifdef ka exact mirror hai.
Verify: compiler sirf int level = 2; dekhta hai. Toh level == 2, aur hidden declare bhi nahi hua. ✅
Worked example Ex 5 — Case E:
#ifndef default with external override
/* compiled with: gcc -DBUFFER_SIZE=1024 prog.c */
#ifndef BUFFER_SIZE
#define BUFFER_SIZE 256
#endif
char buf [BUFFER_SIZE];
buf kitna bada hai?
Forecast: file mein 256 likha hai, command line mein 1024 likha hai. Kaun jeetega?
-D flag pehle define karta hai. -DBUFFER_SIZE=1024 bilkul waise hi hai jaise tumhare code ke shuru mein, pehle hi, #define BUFFER_SIZE 1024 likhna — dekho Build flags -D and the command line .
Yeh step kyun? Command-line defines source se pehle inject hote hain.
#ifndef BUFFER_SIZE ab false hai. Yeh already defined hai (=1024), isliye inner #define BUFFER_SIZE 256 skip ho jaata hai.
Yeh step kyun? Yahan #ifndef ka poora point yahi hai — "default tabhi set karo jab kisine set na kiya ho."
Substitute karo. char buf[BUFFER_SIZE]; → char buf[1024];.
Verify: buf mein 1024 bytes hain. -D flag hatao aur wahi file buf[256] compile karegi. Source edit kiye bina external configuration override karti hai. ✅
Worked example Ex 6 — Case F: include guard double paste ko rokta hai
Files:
/* point.h */
#ifndef POINT_H
#define POINT_H
struct Point { int x, y; };
#endif
/* main.c */
#include "point.h"
#include "point.h" /* jaan-bujhkar do baar include kiya */
Translation unit mein struct Point { ... }; kitni baar appear hota hai?
Forecast: guard ke bina yeh do baar aata hai → "redefinition" error. Guard ke saath?
Pehla #include file paste karta hai. POINT_H undefined hai → #ifndef true → POINT_H define karo, struct rakho.
→ translation unit mein ab struct Point { int x, y; }; ek baar hai.
Doosra #include file phir paste karta hai. Lekin POINT_H ab defined hai → #ifndef false → preprocessor #endif tak sab kuch delete kar deta hai.
Yeh step kyun? Pehli baar define kiya hua tag "already glued" flag ka kaam karta hai.
Verify: struct exactly ek baar appear hoti hai — koi redefinition error nahi. Dekhो Header files and translation units ki ek unit mein ek struct-definition kyun hard limit hai. ✅
Worked example Ex 7 — Case G: degenerate / empty inputs
#define EMPTY
#define STICK ( a , b ) a b
#define ZERO 0
int n = STICK ( 1 , ZERO);
int m = 4 EMPTY + 1 ;
n aur m kya hain?
Forecast: ek macro kuch nahi ke roop mein defined — EMPTY kya paste karta hai?
EMPTY empty string mein expand hota hai. 4 EMPTY + 1 → 4 + 1 (jahan EMPTY tha wahan sirf whitespace).
Yeh step kyun? #define EMPTY bina kisi replacement text ke matlab hai "is token ko delete karo." Ek degenerate lekin legal macro.
STICK(1, ZERO). a=1, b=ZERO paste karo: → 1 ZERO. Phir ZERO khud rescan hoke 0 ban jaata hai: → 1 0.
Yeh step kyun? Lekin 1 0 do integer tokens hain ek space ke saath — woh ek valid expression nahi hai, isliye yeh line ek compile error hogi jab tak hum kuch joinable nahi chahte the.
Intent repair karo. 10 mein join karne ke liye tumhe ## paste operator chahiye hoga: #define STICK(a,b) a##b → 10. Jaise likha hai, n compile nahi hoga.
Verify: m = 4 + 1 = 5 (empty macro cleanly gaaib ho gayi). ## ke bina STICK(1, ZERO) ek degenerate failure hai — jaanna zaroori hai ki "do tokens mein expand hona" ≠ "ek number mein expand hona." ✅ (Hum m aur ## version neeche check karte hain.)
Worked example Ex 8 — Case H: real-world cross-platform word problem
Problem. Tum ek logging function ship karte ho. Windows par newline "\r\n" honi chahiye; Linux/macOS par "\n". Tumhara build Windows par automatically _WIN32 set karta hai. Macro likho aur predict karo ki Linux build ke liye NEWLINE kya emit karega.
#ifdef _WIN32
#define NEWLINE " \r\n "
#else
#define NEWLINE " \n "
#endif
Forecast: Linux build par, kya _WIN32 defined hai?
Poocho: kya _WIN32 yahan defined hai? Linux par compiler _WIN32 predefine nahi karta, isliye #ifdef _WIN32 false hai.
Yeh step kyun? Cross-platform code predefined target macros par branch karta hai — dekho Conditional compilation for cross-platform code .
#else branch bachta hai. Preprocessor #define NEWLINE "\n" rakhta hai aur Windows line ko poori tarah delete kar deta hai.
Verify: Linux par, NEWLINE "\n" (1-character string) mein expand hota hai. Windows par yeh "\r\n" (2 characters) hota. Dono string lengths neeche check ki gayi hain. ✅
Worked example Ex 9 — Case I: exam twist (trailing
; + nested conditions)
#define SIZE 10 ;
#define FLAG
int arr [SIZE]
#ifdef FLAG
;
int extra = SIZE + 1 ;
#endif
Do bugs dhundho aur fixed extra do.
Forecast: SIZE ek semicolon carry kare toh arr[SIZE] kya banta hai?
SIZE → 10; substitute karo. int arr[SIZE] → int arr[10;].
Yeh step kyun? ; replacement text ka hissa tha, isliye woh brackets ke andar land karta hai → syntax error. Bug 1: kisi #define value ko kabhi ; se khatam mat karo.
Doosra use. int extra = SIZE + 1; → int extra = 10; + 1; — phir toota hua (10; statement band karta hai, + 1; stray reh jaata hai).
Yeh step kyun? Wahi trailing-semicolon poison, ab ek expression mein.
Fix karo. #define SIZE 10 (koi semicolon nahi). Phir arr[10] valid hai aur extra = 10 + 1.
Verify: fix ke saath, extra = 11 aur arr mein 10 elements hain. #ifdef FLAG block rakha jaata hai (FLAG defined hai) lekin semicolon bug conditional se alag hai. ✅
Worked example Ex 10 — Case J: limiting case — chained macros & rescanning
#define A B
#define B C
#define C 7
#define TWICE ( x ) ((x) + (x))
int r = TWICE (A);
r kya hai?
Forecast: chain A → B → C → 7 kitni door tak unwind hoti hai?
TWICE(A) expand karo. → ((A) + (A)).
Yeh step kyun? Argument A do baar paste hota hai (Case C flavor), lekin yahan koi side effect nahi hai.
Result ko rescan karo. Preprocessor output ko phir examine karta hai: har A → B, phir B → C, phir C → 7.
→ ((7) + (7)).
Yeh step kyun? Har substitution ke baad preprocessor rescan karta hai aur zyada macros dhundhta hai — yahi chained defines ka "limiting behaviour" hai. Yeh tab rukta hai jab koi macro name nahi bachta (aur infinite recursion se bachne ke liye ek macro ko khud mein expand karne se refuse karta).
Compiler compute karta hai. 7 + 7 = 14.
Verify: r = 14. Poori chain ek preprocessing pass mein resolve ho gayi kyunki rescanning links A→B→C→7 follow karta hai. ✅
Recall Rapid self-test (answers chhupao)
Ex1 RATE*8+2 ::: 42
Ex2 AREA(2+3,4) with w*h body ::: 14 (fix se 20 milta hai)
Ex3 MAX2(i++,3) from i=5 gives m,i ::: m=6, i=7 (double evaluation)
Ex5 with -DBUFFER_SIZE=1024 ::: buf mein 1024 bytes hain
Ex6 struct guard ke saath paste, do baar include ::: ek baar appear hoti hai
Ex8 NEWLINE on Linux ::: "\n"
Ex9 fixed extra ::: 11
Ex10 TWICE(A) through chain ::: 14
Mnemonic Poora matrix ek saans mein
"Paste karo, compute mat karo; parenthesise karo, assume mat karo; guard karo, double mat karo; aur semicolon ek trap hai." Upar har cell inhi chaar sins mein se ek se bacha hua hai.