Chapter 34Advanced

Recursion in C

Functions that call themselves! Learn this powerful technique, understand how it uses the stack, and know when to use it.

22 min readUpdated 2024-12-16
recursionbase caserecursive casestackfactorialfibonacci

What You Will Learn

  • āœ“Understand how recursion works
  • āœ“Write base case and recursive case
  • āœ“Visualize the call stack
  • āœ“Know when recursion is better than loops

01What is Recursion?

šŸ”„ Simple Definition

Recursion is when a function calls itself to solve a problem by breaking it down into smaller, similar problems.

šŸŖž Think of It Like Mirrors

When you stand between two mirrors facing each other, you see infinite reflections — each reflection contains another reflection!

šŸŖžā†šŸ§ā†’šŸŖž

Similarly, a recursive function calls itself, which calls itself, which calls itself... until it stops!

šŸ“š Real-World Examples of Recursion

šŸŖ†

Russian Dolls (Matryoshka)

Open a doll → find smaller doll inside → open it → find smaller doll... until the smallest one!

šŸ“‚

Folder Structure

A folder can contain other folders, which contain other folders... until you reach files!

🌳

Family Tree

Your ancestors: parent → grandparent → great-grandparent... goes back infinitely!

🄦

Broccoli / Fractals

Each branch of broccoli looks like a mini broccoli. The pattern repeats at every scale!

šŸ”¢ Mathematical Example: Factorial

The factorial of a number is a perfect example of recursion:

5! = 5 Ɨ 4!

4! = 4 Ɨ 3!

3! = 3 Ɨ 2!

2! = 2 Ɨ 1!

1! = 1 (stop here!)

Each factorial is defined in terms of a smaller factorial!

02Two Essential Parts of Every Recursive Function

āš ļø IMPORTANT: Both Parts Are Required!

Without both parts, your recursive function will either run forever (crash!) or never work correctly.

1

Base Case

šŸ“

What: The condition that STOPS the recursion

šŸ¤”

Why: Without it, function calls itself forever!

šŸ“

Think: The smallest Russian doll (no doll inside)

if (n <= 1) {

return 1;

}

2

Recursive Case

šŸ“

What: Function calls ITSELF with smaller problem

šŸ¤”

Why: Breaks big problem into smaller ones

šŸ“

Think: Opening to find the next smaller doll

else {

return n * factorial(n - 1);

}

šŸ“ This program shows a complete recursive function with both parts clearly labeled.

factorial_explained.c
C
1#include <stdio.h>
2
3// Recursive function to calculate factorial
4int factorial(int n) {
5
6 // ========== PART 1: BASE CASE ==========
7 // When to STOP calling itself
8 // This prevents infinite recursion!
9 if (n <= 1) {
10 printf(" Base case reached: factorial(%d) = 1\n", n);
11 return 1; // Stop here, return 1
12 }
13
14 // ========== PART 2: RECURSIVE CASE ==========
15 // Call itself with a SMALLER problem
16 // n! = n Ɨ (n-1)!
17 printf(" Calculating: factorial(%d) = %d Ɨ factorial(%d)\n", n, n, n-1);
18 return n * factorial(n - 1); // Calls itself!
19}
20
21int main() {
22 printf("Calculating factorial(5):\n");
23 printf("========================\n");
24
25 int result = factorial(5);
26
27 printf("========================\n");
28 printf("Result: 5! = %d\n", result);
29
30 return 0;
31}
Output

Calculating factorial(5):

========================

Calculating: factorial(5) = 5 Ɨ factorial(4)

Calculating: factorial(4) = 4 Ɨ factorial(3)

Calculating: factorial(3) = 3 Ɨ factorial(2)

Calculating: factorial(2) = 2 Ɨ factorial(1)

Base case reached: factorial(1) = 1

========================

Result: 5! = 120

03How Recursion Uses Stack Memory

šŸ“š What is the Stack?

The stack is a special area in memory that stores information about function calls. Think of it like a stack of plates — you add plates on top and remove from the top!

šŸ½ļø Stack = Stack of Plates

Adding plates (PUSH)

Plate 4 ← Top
Plate 3
Plate 2
Plate 1 ← Bottom

New plates go on TOP

Removing plates (POP)

Plate 4 ← Removed
Plate 3 ← Now Top
Plate 2
Plate 1

Remove from TOP only

šŸ–„ļø What is a Stack Frame?

Every time a function is called, C creates a stack frame (like adding a plate). Each stack frame stores:

šŸ“¦

Local Variables

Variables inside the function

šŸ“„

Parameters

Values passed to function

ā†©ļø

Return Address

Where to go back after

šŸ” Step-by-Step: factorial(3) in Stack Memory

Let's trace exactly what happens in memory when we call factorial(3):

1

Call factorial(3)

Stack grows ↓

factorial(3)

n = 3

waiting for: 3 Ɨ factorial(2)

What happens:

• n=3, not base case (3 > 1)

• Need to calculate 3 Ɨ factorial(2)

• Call factorial(2) first!

2

Call factorial(2)

Stack grows ↓

factorial(2) ← TOP

n = 2

waiting for: 2 Ɨ factorial(1)

factorial(3)

n = 3, waiting...

What happens:

• New frame added on top!

• n=2, not base case (2 > 1)

• Need factorial(1) first!

3

Call factorial(1) — BASE CASE!

Stack at maximum ↓

factorial(1) ← TOP āœ“

n = 1

BASE CASE! Returns 1

factorial(2)

waiting...

factorial(3)

waiting...

What happens:

• n=1, THIS IS BASE CASE!

• No more recursive calls

• Returns 1 immediately

• Stack starts unwinding ↑

4

Return to factorial(2)

Stack shrinks ↑ (POP)

factorial(1) — REMOVED

factorial(2) ← TOP

Got 1 from factorial(1)

Returns: 2 Ɨ 1 = 2

factorial(3)

waiting...

What happens:

• factorial(1) frame removed

• factorial(2) gets answer: 1

• Calculates: 2 Ɨ 1 = 2

• Returns 2

5

Return to factorial(3) — FINAL RESULT!

Stack empty! ↑

factorial(2) — REMOVED

factorial(3) ← TOP

Got 2 from factorial(2)

Returns: 3 Ɨ 2 = 6

What happens:

• factorial(2) frame removed

• factorial(3) gets answer: 2

• Calculates: 3 Ɨ 2 = 6

• Returns 6 — DONE!

šŸ’” Key Insight: LIFO (Last In, First Out)

The last function called is the first to return. factorial(1) was called last but returned first. That's how stacks work!

04See the Stack in Action

šŸ“ This program shows exactly how the stack grows and shrinks during recursion.

stack_visualization.c
C
1#include <stdio.h>
2
3// Global variable to track recursion depth (for visualization)
4int depth = 0;
5
6// Helper function to print indentation
7void printIndent() {
8 for (int i = 0; i < depth; i++) {
9 printf(" │ ");
10 }
11}
12
13int factorial(int n) {
14 depth++; // Going deeper into the stack
15
16 // Show we're entering this function
17 printIndent();
18 printf("šŸ“„ PUSH: factorial(%d) - Stack grows!\n", n);
19
20 // ========== BASE CASE ==========
21 if (n <= 1) {
22 printIndent();
23 printf("šŸ›‘ BASE CASE: n=%d, returning 1\n", n);
24
25 printIndent();
26 printf("šŸ“¤ POP: factorial(%d) returns 1\n", n);
27 depth--; // Stack shrinks
28 return 1;
29 }
30
31 // ========== RECURSIVE CASE ==========
32 printIndent();
33 printf("šŸ”„ Need factorial(%d), calling...\n", n-1);
34
35 int result = n * factorial(n - 1); // Recursive call
36
37 // We're back! Show we're returning
38 printIndent();
39 printf("šŸ“¤ POP: factorial(%d) returns %d Ɨ %d = %d\n", n, n, result/n, result);
40
41 depth--; // Stack shrinks
42 return result;
43}
44
45int main() {
46 printf("╔═══════════════════════════════════════════╗\n");
47 printf("ā•‘ RECURSION STACK VISUALIZATION ā•‘\n");
48 printf("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n\n");
49
50 printf("Calling factorial(4)...\n\n");
51
52 int result = factorial(4);
53
54 printf("\n════════════════════════════════════════════\n");
55 printf("āœ… Final Result: 4! = %d\n", result);
56 printf("════════════════════════════════════════════\n");
57
58 return 0;
59}
Output

╔═══════════════════════════════════════════╗

ā•‘ RECURSION STACK VISUALIZATION ā•‘

ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•

Calling factorial(4)...

│ šŸ“„ PUSH: factorial(4) - Stack grows!

│ šŸ”„ Need factorial(3), calling...

│ │ šŸ“„ PUSH: factorial(3) - Stack grows!

│ │ šŸ”„ Need factorial(2), calling...

│ │ │ šŸ“„ PUSH: factorial(2) - Stack grows!

│ │ │ šŸ”„ Need factorial(1), calling...

│ │ │ │ šŸ“„ PUSH: factorial(1) - Stack grows!

│ │ │ │ šŸ›‘ BASE CASE: n=1, returning 1

│ │ │ │ šŸ“¤ POP: factorial(1) returns 1

│ │ │ šŸ“¤ POP: factorial(2) returns 2 Ɨ 1 = 2

│ │ šŸ“¤ POP: factorial(3) returns 3 Ɨ 2 = 6

│ šŸ“¤ POP: factorial(4) returns 4 Ɨ 6 = 24

════════════════════════════════════════════

āœ… Final Result: 4! = 24

════════════════════════════════════════════

05Stack Overflow: When Things Go Wrong! šŸ’„

āš ļø What is Stack Overflow?

The stack has limited memory. If recursion goes too deep (too many function calls without returning), the stack runs out of space and the program crashes!

āŒ Causes of Stack Overflow

  • 1.Missing base case — function never stops
  • 2.Wrong base case — condition never becomes true
  • 3.Too deep recursion — even with base case

āœ… How to Prevent

  • 1.Always have a base case
  • 2.Make sure problem gets smaller each call
  • 3.Use iteration for very deep recursion

šŸ“ This program shows what happens when there's no base case (DON'T RUN THIS!).

stack_overflow.c
C
1#include <stdio.h>
2
3// āš ļø DANGER: This will crash!
4// Missing base case = infinite recursion
5
6void infiniteRecursion(int n) {
7 printf("Call #%d - Stack growing...\n", n);
8
9 // NO BASE CASE! Never stops!
10 infiniteRecursion(n + 1); // Keeps calling forever
11
12 // This line is never reached
13 printf("This never prints\n");
14}
15
16// DON'T run this!
17// int main() {
18// infiniteRecursion(1);
19// return 0;
20// }
21
22// āœ… CORRECT VERSION:
23void safeRecursion(int n, int limit) {
24 printf("Call #%d\n", n);
25
26 // BASE CASE: Stop when we reach the limit
27 if (n >= limit) {
28 printf("Base case reached! Stopping.\n");
29 return;
30 }
31
32 // RECURSIVE CASE: Call with n+1
33 safeRecursion(n + 1, limit);
34}
35
36int main() {
37 printf("Safe recursion with base case:\n");
38 safeRecursion(1, 5);
39 return 0;
40}
Output

Safe recursion with base case:

Call #1

Call #2

Call #3

Call #4

Call #5

Base case reached! Stopping.

06Types of Recursion

TypeDescriptionExample
Direct RecursionFunction calls itself directlyfactorial(n-1)
Indirect RecursionA calls B, B calls AfuncA() → funcB() → funcA()
Tail RecursionRecursive call is the LAST operationreturn factorial(n-1, acc*n)
Head RecursionRecursive call is the FIRST operationfactorial(n-1); then process
Tree RecursionFunction makes MULTIPLE recursive callsfib(n-1) + fib(n-2)

šŸ“ Tail recursion example — the recursive call is the last thing the function does.

recursion_types.c
C
1#include <stdio.h>
2
3// ========== TAIL RECURSION ==========
4// The recursive call is the LAST operation
5// Can be optimized by compilers (uses less stack)
6
7int factorial_tail(int n, int accumulator) {
8 if (n <= 1) {
9 return accumulator; // Base case
10 }
11 // Tail call - nothing to do after this returns
12 return factorial_tail(n - 1, n * accumulator);
13}
14
15// Wrapper function for easy use
16int factorial(int n) {
17 return factorial_tail(n, 1);
18}
19
20// ========== TREE RECURSION ==========
21// Function makes MULTIPLE recursive calls (like branches of a tree)
22// Example: Fibonacci
23
24int fibonacci(int n) {
25 if (n <= 1) {
26 return n; // Base cases: F(0)=0, F(1)=1
27 }
28 // TWO recursive calls - creates a tree!
29 return fibonacci(n - 1) + fibonacci(n - 2);
30}
31
32int main() {
33 printf("=== Tail Recursion (Factorial) ===\n");
34 for (int i = 1; i <= 5; i++) {
35 printf("%d! = %d\n", i, factorial(i));
36 }
37
38 printf("\n=== Tree Recursion (Fibonacci) ===\n");
39 printf("First 10 Fibonacci numbers:\n");
40 for (int i = 0; i < 10; i++) {
41 printf("F(%d) = %d\n", i, fibonacci(i));
42 }
43
44 return 0;
45}
Output

=== Tail Recursion (Factorial) ===

1! = 1

2! = 2

3! = 6

4! = 24

5! = 120

=== Tree Recursion (Fibonacci) ===

First 10 Fibonacci numbers:

F(0) = 0

F(1) = 1

F(2) = 1

F(3) = 2

F(4) = 3

F(5) = 5

F(6) = 8

F(7) = 13

F(8) = 21

F(9) = 34

07Recursion vs Iteration: When to Use Which?

AspectRecursionIteration (Loops)
MemoryUses more (stack frames)Uses less
SpeedUsually slowerUsually faster
Code ReadabilityMore elegant for some problemsCan be complex for some problems
RiskStack overflow possibleNo stack overflow
Best ForTrees, graphs, divide-conquerSimple counting, arrays

šŸ”„ Use Recursion When:

  • • Problem has natural recursive structure
  • • Working with trees/graphs
  • • Divide-and-conquer algorithms
  • • Code clarity is priority
  • • Depth is limited

šŸ” Use Iteration When:

  • • Simple counting or looping
  • • Performance is critical
  • • Memory is limited
  • • Depth is unpredictable
  • • Problem is naturally iterative

šŸ“ Same problem solved with both recursion and iteration (factorial).

recursion_vs_iteration.c
C
1#include <stdio.h>
2
3// ========== RECURSIVE VERSION ==========
4int factorial_recursive(int n) {
5 if (n <= 1) return 1;
6 return n * factorial_recursive(n - 1);
7}
8
9// ========== ITERATIVE VERSION ==========
10int factorial_iterative(int n) {
11 int result = 1;
12 for (int i = 2; i <= n; i++) {
13 result *= i;
14 }
15 return result;
16}
17
18int main() {
19 int n = 5;
20
21 printf("Calculating %d!\n\n", n);
22
23 printf("Recursive: %d! = %d\n", n, factorial_recursive(n));
24 printf("Iterative: %d! = %d\n", n, factorial_iterative(n));
25
26 printf("\nBoth give the same answer!\n");
27 printf("But iterative uses less memory.\n");
28
29 return 0;
30}
Output

Calculating 5!

Recursive: 5! = 120

Iterative: 5! = 120

Both give the same answer!

But iterative uses less memory.

08Common Recursion Mistakes

āŒ Mistake 1: Missing Base Case

Without a base case, recursion never stops = stack overflow!

WRONG:

int sum(int n) {
    return n + sum(n-1);
    // Never stops!
}

CORRECT:

int sum(int n) {
    if (n <= 0) return 0;
    return n + sum(n-1);
}

āŒ Mistake 2: Base Case Never Reached

If the recursive case doesn't move toward the base case, it's still infinite!

WRONG:

int bad(int n) {
    if (n == 0) return 0;
    return bad(n + 1);
    // Goes 5,6,7... never 0!
}

CORRECT:

int good(int n) {
    if (n == 0) return 0;
    return good(n - 1);
    // Goes 5,4,3,2,1,0 āœ“
}

āŒ Mistake 3: Wrong Return Value

Forgetting to return the recursive result loses the answer!

WRONG:

int factorial(int n) {
    if (n <= 1) return 1;
    n * factorial(n-1);
    // Missing return!
}

CORRECT:

int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n-1);
}

āŒ Mistake 4: Inefficient Tree Recursion

Fibonacci with simple recursion calculates same values many times!

INEFFICIENT:

int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1)+fib(n-2);
    // fib(3) calculated many times!
}

BETTER (memoization):

int memo[100] = {0};
int fib(int n) {
    if (n <= 1) return n;
    if (memo[n]) return memo[n];
    return memo[n]=fib(n-1)+fib(n-2);
}

09Summary

šŸŽÆ Key Takeaways

  • •Recursion: Function that calls itself to solve smaller sub-problems
  • •Base Case: Condition to STOP recursion (required!)
  • •Recursive Case: Function calls itself with smaller problem
  • •Stack: Memory area that stores function call information
  • •Stack Frame: Info for one function call (params, local vars, return address)
  • •LIFO: Last In, First Out - last called function returns first
  • •Stack Overflow: Crash when recursion goes too deep

12Next Steps

Now that you understand recursion and the stack, explore function pointers!