CS Atlas GATE 2027
PDS course
0 / 103 complete

PDS/Phase 1 · Programming in C

Lesson 1.8 · Item 33 of 103

Preprocessor macros and their expansion traps; function pointers

Read at your own pace. Try the questions before opening the answers.

On this page

0 · Recall Warm-up

Answer from memory first. Answers are in the Answer Vault at the bottom.

  • What is a memory leak? from Lesson 1.7
  • What is sizeof("abc")? from Lesson 1.5.3
  • Inside void f(int *p, int *q) { p = q; *p = 2; } called as f(&i, &j), which variable changes? from Lesson 1.4.2

1 · The Hook

🎣

Before your C file reaches the compiler, a helper called the preprocessor edits it like a find-and-replace tool. Used carelessly, find-and-replace produces surprises: replace “SQ(x)” with “xx” and SQ(2+3) becomes 2+32+3. This lesson covers that tool, and one more ability: storing a function in a variable.

2 · Predict First

🤔

What does this print?

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

int main(void)
{
    printf("%d %d %d\n", SQ(2 + 3), SQ2(2 + 3), 100 / SQ2(5));
    return 0;
}

Make a guess in your head. The answer opens Section 3.

3 · Core Concept

💡

Predict First — the answer

Output

11 25 4

SQ(2 + 3) is replaced by 2 + 3 * 2 + 3 = 11, because a macro substitutes text, not values. SQ2 puts brackets around each use and around the whole: ((2 + 3) * (2 + 3)) = 25, and 100 / ((5) * (5)) = 4.

The preprocessor

Lines starting with # are handled by the preprocessor before compilation: #include pastes in a file, #define defines a macro, #ifdef … #endif keeps or drops code.

Object-like macros

#define N 10 replaces every later N with 10. There is no type and no memory, just text. With #define AREA N * N + 1, the expression 2 * AREA becomes 2 * 10 * 10 + 1.

Function-like macros and their traps

#define SQ(x) x * x pastes the argument text where x appears. Three traps:

  • Missing brackets around parameters: SQ(2 + 3) → 2 + 3 * 2 + 3 = 11.
  • Missing outer brackets: with #define SQ3(x) (x) * (x), 100 / SQ3(5) → 100 / (5) * (5) = 100.
  • Side effects repeated: SQ2(i++) becomes ((i++) * (i++)): i changes twice: undefined behavior.

Safe style: bracket every parameter and the whole body, and never pass ++ or function calls with side effects.

#define SQ3(x) (x) * (x)

int main(void)
{
    printf("%d\n", 100 / SQ3(5));
    return 0;
}

Output

100

Macro vs function

MacroFunction
Whentext replaced before compilingcalled at run time
Types checkednoyes
Argumentspasted as text (may be evaluated twice)evaluated once and copied
Costno call overhead, bigger codecall overhead, one copy of the code

Function pointers

A function’s name decays to its address, which can be stored in a function pointer:

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

int apply(int (*op)(int, int), int x, int y)
{
    return op(x, y);
}

int main(void)
{
    int (*f)(int, int) = add;
    int (*table[2])(int, int) = {add, mul};
    printf("%d %d %d %d\n", f(2, 3), (*f)(4, 5), table[1](3, 4), apply(mul, 6, 7));
    return 0;
}

Output

5 9 12 42
  • int (*f)(int, int); — f is a pointer to a function taking two ints and returning int. The brackets matter: int *f(int, int); would declare a function returning int *.
  • Call it as f(2, 3) or (*f)(2, 3).
  • Arrays of function pointers make tables of operations; passing one as an argument (a callback) lets a function such as a sorter use a comparison you supply.

4 · Visual

Expansion is text substitution:

#define SQ(x)   x * x
   SQ(2 + 3)   ->   2 + 3 * 2 + 3   =  2 + 6 + 3  = 11

#define SQ2(x)  ((x) * (x))
   SQ2(2 + 3)  ->   ((2 + 3) * (2 + 3))           = 25

#define SQ3(x)  (x) * (x)
   100 / SQ3(5) ->  100 / (5) * (5)  = 20 * 5    = 100     (left to right!)

source.c --[preprocessor: #include, #define]--> expanded text --[compiler]--> executable

Write the expansion out in full, then apply ordinary precedence.

Diagram: source with #define · what you write · preprocessor · replaces text · expanded source · no # lines left · compiler · checks types, builds

Macros are gone before the compiler sees the program.

5 · Worked Examples

Example 1 Easy

What does this print?

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define N 4
#define AREA N * N + 1

int main(void)
{
    int x = 3, y = 7;
    printf("%d %d %d\n", MAX(x, y), 2 * AREA, MAX(x + 1, y - 4));
    return 0;
}

Solution

MAX(x, y) = 7. 2 * AREA → 2 * 4 * 4 + 1 = 33. MAX(x + 1, y − 4) → ((x + 1) > (y − 4) ? … ) → 4 > 3 → 4.

Output

7 33 4

Example 2 Medium

What does this print?

#define CUBE(x) (x * x * x)

int main(void)
{
    int a = 2;
    printf("%d\n", CUBE(a + 1));
    return 0;
}

Solution

CUBE(a + 1) → (a + 1 * a + 1 * a + 1) = 2 + 2 + 2 + 1 = 7, not 27.

Output

7

Example 3 GATE

What does this print?

#define PROD(a, b) a * b
#define DOUBLE(x) x + x

int main(void)
{
    int k = 2;
    printf("%d %d\n", PROD(k + 1, k + 2), 3 * DOUBLE(k));
    return 0;
}

Solution

PROD(k + 1, k + 2) expands to k + 1 * k + 2. Multiplication first: 1 * k = 2, so 2 + 2 + 2 = 6 (not 3 × 4 = 12). 3 * DOUBLE(k) expands to 3 * k + k = 6 + 2 = 8 (not 12).

Output

6 8

6 · GATE Traps

⚠️

  • Macros substitute text: always expand fully before evaluating.
  • Bracket every parameter and the whole macro body.
  • Arguments with side effects (i++, function calls) may be evaluated more than once.
  • An object-like macro such as N * N + 1 is not bracketed either: 2 * AREA ≠ 2 * (N * N + 1).
  • int (*f)(int) is a function pointer; int *f(int) is a function returning a pointer.
  • Macros have no type checking and no scope.

8 · Memory Hook

🧠

“Macros are a photocopier, not a calculator.” They copy your text exactly where the parameter was, brackets and all — or without them.

9 · Connect the Dots

Phase 1 is complete: values, operators, control flow, functions, the call stack, pointers, arrays, strings, structures, the heap and the preprocessor. Functions have so far called only other functions. Next phase begins with the question: what happens when a function calls itself?

10 · Self-Test

GATE format. Try every question before opening the vault.

Q1MCQ1 markMacro

What does this print?

#define CUBE(x) (x * x * x)

int main(void)
{
    int a = 2;
    printf("%d\n", CUBE(a + 1));
    return 0;
}
  • (A) 8
  • (B) 7
  • (C) 27
  • (D) 9

Q2NAT2 marksMacro

With #define SQ3(x) (x) * (x), what is 100 / SQ3(5)?

Numerical answer: ________

Q3MCQ1 markDeclarations

int (*op)(int, int); declares op as:

  • (A) a macro
  • (B) a pointer to a function taking two ints and returning int
  • (C) a function returning a pointer to int
  • (D) an array of two ints

Q4MCQ2 marksFunction pointers

What does this print?

int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

int apply(int (*op)(int, int), int x, int y)
{
    return op(x, y);
}

int main(void)
{
    int (*f)(int, int) = add;
    int (*table[2])(int, int) = {add, mul};
    printf("%d %d %d %d\n", f(2, 3), (*f)(4, 5), table[1](3, 4), apply(mul, 6, 7));
    return 0;
}
  • (A) 6 20 12 42
  • (B) 5 9 12 42
  • (C) 5 20 7 13
  • (D) 5 9 7 42

Q5MSQ1 markMacros vs functions

Which are true?

  • (A) A function argument is evaluated exactly once.
  • (B) A macro argument with ++ may be incremented more than once.
  • (C) Macro arguments are type-checked.
  • (D) A macro is expanded before compilation.

11 · Rule Card

  • Preprocessor runs before compiling: #include, #define, #ifdef.
  • #define is text substitution: expand fully, then apply precedence.
  • Bracket parameters and the whole body: #define SQ(x) ((x) * (x)).
  • Side-effect arguments may be evaluated several times (undefined for ++).
  • int (*f)(int, int) = add; f(2, 3) or (*f)(2, 3).
  • Function pointer arrays and callbacks; int *f(int) is different.

12 · Concept Ledger

Unlocked in this lesson. From here on, these terms may be used.

preprocessor#defineobject-like macrofunction-like macromacro expansionfunction pointercallbackarray of function pointers

13 · Answer Vault

Answer Vault

Try the questions first. Open when you are ready to check.

Recall Warm-up

  • Losing the last pointer to a heap block without freeing it.
  • 4 (three characters and '\0').
  • j.

Self-Test

Q1 Answer: (B)1 mark

a + 1 * a + 1 * a + 1 = 2 + 2 + 2 + 1 = 7.

Output

7
  • (A) is wrong: Expand the text fully.
  • (C) is wrong: Would need brackets around x.
  • (D) is wrong: Not (a + 1) squared.

Re-read: Lesson 1.8 — Preprocessor macros and their expansion traps; function pointers

Q2 Answer: 1002 marks

100 / (5) * (5) = 20 × 5 = 100.

Re-read: Lesson 1.8 — Preprocessor macros and their expansion traps; function pointers

Q3 Answer: (B)1 mark

The brackets bind * to op first.

  • (A) is wrong: Macros use #define.
  • (C) is wrong: That is int *op(int, int).
  • (D) is wrong: No array brackets.

Re-read: Lesson 1.8 — Preprocessor macros and their expansion traps; function pointers

Q4 Answer: (B)2 marks

add(2, 3) = 5; add(4, 5) = 9; table[1] = mul: 12; apply(mul, 6, 7) = 42.

Output

5 9 12 42
  • (A) is wrong: add(2, 3) = 5.
  • (C) is wrong: f is add, not mul.
  • (D) is wrong: table[1] is mul: 3 × 4 = 12.

Re-read: Lesson 1.8 — Preprocessor macros and their expansion traps; function pointers

Q5 Answer: (A) (B) (D)1 mark

Macros have no type checking.

  • (C) is wrong: No types exist at expansion time.

Re-read: Lesson 1.8 — Preprocessor macros and their expansion traps; function pointers

Ready for the next idea?

Mark this item complete when you’ve finished reading and working through it.