C Programming - Beginner to Brilliance / Preprocessors and Macros

Macros with Arguments

A macro with arguments is a code template defined using #define that looks like a function, but it's handled by the preprocessor before compilation.

Instead of calling a function at runtime, the macro is replaced inline with the actual code during preprocessing.


📌 Syntax

#define MACRO_NAME(parameter1, parameter2, ...) expression_using_parameters

✅ Example: Square of a Number

#define SQUARE(x) ((x) * (x))
int main() {
    int a = 5;
    printf("Square: %d\\n", SQUARE(a)); // Becomes ((a) * (a)) → (5 * 5)
}

🔄 The macro SQUARE(a) is replaced by ((a) * (a)) before compilation.


⚠️ Common Pitfalls & How to Avoid Them

❌ Without Parentheses – Bug!

#define SQUARE(x) x * x
int result = SQUARE(1 + 2);  // Becomes 1 + 2 * 1 + 2 = 1 + 2 + 2 = 5 ❌

🧠 What actually happened?

result = 1 + 2 * 1 + 2
        = 1 + 2 + 2      // (due to operator precedence)
        = 5 (Wrong!)

✅ Fix: Wrap Parameters & Expression in Parentheses

#define SQUARE(x) ((x) * (x))  // Now: ((1 + 2) * (1 + 2)) = 9 ✔️

🧪 More Examples

🔸 Maximum of Two Numbers

#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = 5, y = 9;
printf("Max: %d", MAX(x, y)); // Output: 9

🔸 Swap Two Values – Won’t Work as Expected!

#define SWAP(a, b) { int temp = a; a = b; b = temp; }
int x = 10, y = 20;
SWAP(x, y);

🧨 May work, but:

  • It creates a variable named temp every time → Name collision risk!

  • Multiple statements inside {} can be dangerous in if-else blocks

✅ Better (with do { } while(0) trick):

define SWAP(a, b) do { int temp = a; a = b; b = temp; } while(0)

This makes it behave more like a real function!


🔸 Area of a Circle

#define PI 3.14159
#define AREA_CIRCLE(r) (PI * (r) * (r))
float area = AREA_CIRCLE(5); // (3.14159 * 5 * 5)

🔸 Min of Three Numbers

#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))

🧱 Macro Expansion Process (Step-by-step)

#define MULT(a, b) ((a) * (b))

int x = 2;
int y = 3;
int z = MULT(x + 1, y - 1);

🔍 The compiler sees:

int z = ((x + 1) * (y - 1)); // Safe and correct

But without parentheses:

#define MULT(a, b) (a * b)

int z = MULT(x + 1, y - 1); // Becomes: (x + 1 * y - 1)
                            // Wrong due to operator precedence

📌 Key Takeaways – Best Practices

Do ✅

Don’t ❌

Wrap parameters in ()

Forget parentheses

Wrap whole expression in ()

Write long, complex logic

Use simple expressions

Use multiple statements

Use do { } while(0) for blocks

Assume macros do type-checking


💥 Macro vs Function: When to Use?

Aspect

Macro with Arguments

Function

Speed

Faster (inline substitution)

Slightly slower (call stack)

Type safety

❌ No type checking

✅ Type checking

Debugging

❌ Hard to debug

✅ Easier to trace

Complexity

Good for 1-line expressions

Good for multi-step logic