5.1.28 · D4 · HinglishC Programming

ExercisesMacros vs inline functions

2,881 words13 min read↑ Read in English

5.1.28 · D4 · Coding › C Programming › Macros vs inline functions

Do rules hain jo hum baar baar use karenge, toh unhe ek baar naam de lete hain:

Neeche har trap in do rules mein se kisi ek ka hi result hai.


Level 1 — Recognition

L1.1 — Use kaun karta hai?

Har line ke liye batao ki preprocessor zimmedar hai ya compiler.

#define PI 3.14159        // (a)
static inline int sq(int x){ return x*x; }  // (b)
int y = sq(5);            // (c) — inline karne ka faisla kaun karta hai?
Recall Solution L1.1
  • (a) Preprocessor. #define ek directive hai; yeh real compilation se pehle run hota hai aur bas yaad rakhta hai ki "token PI ko text 3.14159 se replace karo."
  • (b) Compiler. inline ek real C keyword hai jo compiler dwara parse aur type-check hota hai.
  • (c) Compiler (uska optimizer) decide karta hai ki body ko yahan actually paste karna hai ya nahi. inline sirf ek hint hai.

L1.2 — Text ya value?

#define DBL(x) (x)+(x). Koi likhta hai DBL(3). Preprocessor kaun sa text produce karta hai (number nahi)?

Recall Solution L1.2

Pure text substitution: (3)+(3). Preprocessor kabhi 6 compute nahi karta; woh sirf text rewrite karta hai. Compiler baad mein (3)+(3) = 6 evaluate karta hai.

L1.3 — Address kaun le sakta hai?

Tumhe "square" behaviour reuse karne ke liye ek pointer chahiye: int (*f)(int) = ???. Kya yeh macro SQUARE ho sakta hai? Kya sq ho sakta hai?

Recall Solution L1.3

Sirf inline function sq ka address hota hai, isliye int (*f)(int) = sq; legal hai. Macro ek object nahi hai — preprocessing ke baad woh gayab ho jaata hai — isliye &SQUARE meaningless hai. Yeh ek power hai jo inline functions ke paas hai aur macros ke paas kabhi nahi hogi.


Level 2 — Application

L2.1 — Precedence trap

#define SQ(x) x*x
int r = SQ(2 + 3);

Text ko hand-expand karo, phir r ki numeric value batao. Author kya chahta tha?

Recall Solution L2.1

Blind paste: SQ(2 + 3)2 + 3*2 + 3. Ab operator precedence apply karo — * + se pehle bind karta hai: Toh r = 11. Author chahta tha . Missing parentheses ne bahari + ko argument ko tod dene diya.

L2.2 — Fix karo

SQ ko rewrite karo taaki SQ(2 + 3) se 25 mile, phir prove karne ke liye re-expand karo.

Recall Solution L2.2

Poori body aur har parameter ko parenthesize karo:

#define SQ(x) ((x)*(x))

SQ(2 + 3) expand karo → ((2 + 3)*(2 + 3)). ✅ Inner parens 2+3 ko intact rakhte hain; outer parens result ko protect karte hain agar macro kisi bade expression ke andar ho jaise 1 - SQ(2).

L2.3 — Outer-paren check

Fixed #define SQ(x) ((x)*(x)) ke saath, int r = 100 / SQ(2); evaluate karo. Phir wahi body ke saath evaluate karo jisme outer parens bhool gaye, #define BADSQ(x) (x)*(x).

Recall Solution L2.3
  • Good: 100 / ((2)*(2)) = 100 / 4 = 25.
  • Bad (BADSQ): 100 / (2)*(2)/ aur * left-associative aur equal precedence hain, toh yeh hai (100 / 2) * 2 = 50 * 2 = 100. Missing outer parens ne surrounding / ko macro ke andar ghusne diya. Answer: 25 vs 100.

Level 3 — Analysis

L3.1 — Double evaluation, counted

#define SQ(x) ((x)*(x))
int i = 4;
int r = SQ(++i);

UB concerns ko ignore karte hue aur left-to-right evaluation assume karte hue, i kitni baar increment hoga, aur final i aur r kya honge?

Recall Solution L3.1

Expand: SQ(++i)((++i)*(++i)). Text ++i do baar aata hai, toh i do baar increment hota hai: 4 → 5 → 6, giving i = 6. Naive left-to-right ke under product hai 5 * 6 = 30. Honest caveat: i ko do baar modify karna bina kisi sequence point ke genuinely undefined behavior hai; ek real compiler kuch bhi print kar sakta hai. Lesson yeh hai: macro side effect duplicate karta hai. (30 yahan sirf double increment expose karne ke liye hai, guaranteed value nahi.)

L3.2 — Inline version, counted

static inline int sq(int x){ return x*x; }
int i = 4;
int r = sq(++i);

Ab i kitni baar increment hoga, aur final i aur r kya honge?

Recall Solution L3.2

Evaluate-once rule ke hisaab se, ++i body run hone se pehle parameter x mein ek baar compute hota hai: i 5 ho jaata hai, x mein 5 aa jaata hai. Body: x*x = 25. Toh i = 5, r = 25. Defined, predictable, type-checked. Yahi woh headline reason hai kyun inline functions prefer karte hain.

L3.3 — MAX re-evaluation

#define MAX(a,b) ((a) > (b) ? (a) : (b))
int p = 3, q = 5;
int m = MAX(p++, q++);

Expansion trace karo aur, left-to-right assume karte hue, m, p, q do.

Recall Solution L3.3

Expand: ((p++) > (q++) ? (p++) : (q++)).

  • Compare: p=3, q=5 read karta hai, phir dono post-increment hote hain → compare ke baad p=4, q=6. Condition 3 > 5 false hai.
  • Toh ?: teesra branch (q++) select karta hai: current q=6 result ke roop mein read karta hai, phir increment → q=7. Result: m = 6, p = 4, q = 7. "Winner" q do baar evaluate hua — ek baar compare mein, ek baar return hone pe. (Formally phir se UB; double eval expose karne ke liye dikhaya.)

Level 4 — Synthesis

L4.1 — Dangling-if bug

#define SWAP(a,b) int t=(a); (a)=(b); (b)=t;
if (cond) SWAP(x,y); else reset();

Explain karo yeh compile kyun fail hota hai / misbehave karta hai, aur SWAP ko sahi se rewrite karo.

Recall Solution L4.1

if ke baad expand karo:

if (cond) int t=(x); (x)=(y); (y)=t; else reset();

if body sirf pehla statement int t=(x); hai. Baaki do lines unconditionally run hoti hain, aur phir ek bare else aata hai bina matching if ke → compile error. Fix karo do{...}while(0) idiom se, jo kai statements ko ek mein pack karta hai:

#define SWAP(a,b,T) do { T t=(a); (a)=(b); (b)=t; } while(0)
if (cond) SWAP(x,y,int); else reset();   // ek statement, ; consume ho gaya

do{...}while(0) ek single statement hai jo ek baar run hota hai, isliye if/else pairing preserve hoti hai aur trailing ; use cleanly close karta hai.

L4.2 — Sahi tool choose karo

Har zaroorat ke liye, macro ya inline function choose karo aur ek line mein justify karo: (a) MIN jo same program mein int, double, aur long ke liye kaam kare; (b) ek helper is_even(n) jo hot loop mein lakhon baar use ho aur debuggable bhi ho; (c) ek token ko string literal mein turn karna, jaise STR(hello)"hello"; (d) ek compile-time named constant BUFSIZE = 1024 jiska aap typed pointer bhi le sako.

Recall Solution L4.2
  • (a) Macro. C mein generics nahi hote; #define MIN(a,b) ((a)<(b)?(a):(b)) type-agnostic text hai. (Parenthesize karo!)
  • (b) Inline function. Inline hone pe koi call overhead nahi, single evaluation, aur breakpoints set kar sakte ho. Debuggability macro ko rule out karti hai.
  • (c) Macro. #x se stringizing ek preprocessor-only power hai: #define STR(x) #x. Koi function yeh nahi kar sakta.
  • (d) const / constexpr-style constant. static const int BUFSIZE = 1024; prefer karo (dekho const and constexpr) — yeh typed aur addressable hai; ek #define constant ka koi address nahi hota.

L4.3 — Macro ko safe inline mein rewrite karo

Diya gaya #define CUBE(x) ((x)*(x)*(x)), ek equivalent int-typed inline function likho, aur ek behaviour state karo jo CUBE(i++) ke liye badalta hai.

Recall Solution L4.3
static inline int cube(int x){ return x*x*x; }

Macro ke saath, CUBE(i++) i++ teen baar paste karta hai → i teen baar increment hota hai (UB). cube(i++) ke saath, i ek baar increment hota hai, x us single value ko capture karta hai, aur x*x*x use teen baar safely use karta hai. Maano i = 2: cube(i++) 2*2*2 = 8 return karta hai aur i = 3 chhod jaata hai.


Level 5 — Mastery

L5.1 — Fixed macro aur side effects ke saath full trace

#define SQ(x) ((x)*(x))
int a = 2;
int b = SQ(a + 1);
int c = SQ(a++);

Left-to-right evaluation assume karte hue, b, c, aur final a do. Mark karo kaunsi line UB hai.

Recall Solution L5.1
  • b = SQ(a + 1)((a + 1)*(a + 1)) with a = 2(3)*(3) = 9. Koi side effect nahi, defined. b = 9.
  • c = SQ(a++)((a++)*(a++)). a++ do baar aata hai → naive result 2 * 3 = 6, a end pe 4 hota hai. Yeh line undefined behavior hai (koi sequence point nahi hote hue a ke do modifications); c = 6, a = 4 sirf double increment expose karne ke liye dikhaya gaya hai.

L5.2 — Macro vs inline ka difference predict karo

L5.1 ke same inputs ke saath lekin static inline int sq(int x){return x*x;} ke saath:

int a = 2;
int b = sq(a + 1);
int c = sq(a++);

b, c, aur final a do, aur batao har line ab well-defined kyun hai.

Recall Solution L5.2
  • b = sq(a + 1): argument a+1 = 3 ek baar compute → 3*3 = 9. b = 9.
  • c = sq(a++): a++ ek baar evaluate hota hai — parameter x ko 2 milta hai, phir a 3 ho jaata hai. Body x*x = 2*2 = 4. c = 4, a = 3.
  • Har line defined hai kyunki evaluate-once rule har side effect ko body run hone se pehle ek single, sequenced execution deta hai. L5.1 ke UB se compare karo.

L5.3 — #define constant vs typed constant

Tumhe SIZE = 10 chahiye jo int buf[SIZE]; declare karne ke liye aur printf("%d", SIZE); mein pass karne ke liye use ho. (a) Macro version dikhao aur batao printf ko kya milta hai. (b) Typed version dikhao aur ek advantage batao.

Recall Solution L5.3
  • (a) #define SIZE 10. int buf[SIZE];int buf[10];, printf("%d", SIZE);printf("%d", 10);. 10 print karta hai. Purely textual, untyped.
  • (b) static const int SIZE = 10;. Same array size (C99+ mein yeh VLA-style hai ya strict C ke liye enum/macro use karo array bounds ke liye; typed constant fully addressable hai). Advantage: iska ek type hai, ek address hai (&SIZE), scope respect karta hai, aur debugger mein naam se dikhta hai — yeh sab cheezein koi #define constant offer nahi karta.

L5.4 — Design challenge: safe generic MAX

Macro MAX(a,b) double-evaluate karta hai. Ek technique sketch karo (words + code mein) jo macro ki genericity rakhti hai lekin har argument ko ek baar evaluate karti hai, GNU statement expressions use karke.

Recall Solution L5.4

Ek statement expression use karo (GCC/Clang extension) jo har argument ko typeof use karke ek typed temporary mein store karta hai, taaki har ek ek baar evaluate ho:

#define MAX(a,b) ({ __typeof__(a) _x=(a); __typeof__(b) _y=(b); \
                     _x > _y ? _x : _y; })

_x=(a) aur _y=(b) apne argument ko exactly ek baar ek local mein evaluate karte hain, phir comparison locals use karta hai. MAX(p++, q++) ab p, q mein se har ek ko ek baar increment karta hai — dono worlds ka best, non-portable hone ki cost pe (extension, standard C nahi). Portable code ke liye, per type ek typed inline function prefer karo. Mnemonic: capture-once, compare-locals.


Score-yourself figure

Figure — Macros vs inline functions

Upar ka bar chart dikhata hai, recurring SQ(a++) example ke liye a = 2 ke saath, ki macro aur inline results kaise diverge karte hain — macro double-counts karta hai increment ko (red mein extra height), inline clean rehta hai (green).


Recall Har level ka one-line summary

Recognition (use kaun karta hai) → Application (parenthesize karo) → Analysis (evaluations count karo) → Synthesis (do{}while(0), sahi tool) → Mastery (typed constants + capture-once generics). Har trap = blind paste ya duplicated side effect.

Connections

  • C Preprocessor — woh engine jo blind text paste karta hai
  • Operator Precedence in C — L2 ke precedence traps
  • Function Call Overhead — woh cost jo dono tools remove karte hain
  • Undefined Behavior in C — double-evaluation lines (L3, L5)
  • const and constexpr#define constants ke typed replacements
  • Compiler Optimization — kyun inline sirf ek hint hai