Recursion in C
Functions that call themselves! Learn this powerful technique, understand how it uses the stack, and know when to use it.
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.
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;
}
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.
1#include <stdio.h>23// Recursive function to calculate factorial4int factorial(int n) {5 6 // ========== PART 1: BASE CASE ==========7 // When to STOP calling itself8 // This prevents infinite recursion!9 if (n <= 1) {10 printf(" Base case reached: factorial(%d) = 1\n", n);11 return 1; // Stop here, return 112 }13 14 // ========== PART 2: RECURSIVE CASE ==========15 // Call itself with a SMALLER problem16 // 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}2021int 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}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)
New plates go on TOP
Removing plates (POP)
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):
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!
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!
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 ā
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
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.
1#include <stdio.h>23// Global variable to track recursion depth (for visualization)4int depth = 0;56// Helper function to print indentation7void printIndent() {8 for (int i = 0; i < depth; i++) {9 printf(" ā ");10 }11}1213int factorial(int n) {14 depth++; // Going deeper into the stack15 16 // Show we're entering this function17 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 shrinks28 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 call36 37 // We're back! Show we're returning38 printIndent();39 printf("š¤ POP: factorial(%d) returns %d Ć %d = %d\n", n, n, result/n, result);40 41 depth--; // Stack shrinks42 return result;43}4445int 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}āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā 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!).
1#include <stdio.h>23// ā ļø DANGER: This will crash!4// Missing base case = infinite recursion56void infiniteRecursion(int n) {7 printf("Call #%d - Stack growing...\n", n);8 9 // NO BASE CASE! Never stops!10 infiniteRecursion(n + 1); // Keeps calling forever11 12 // This line is never reached13 printf("This never prints\n");14}1516// DON'T run this!17// int main() {18// infiniteRecursion(1);19// return 0;20// }2122// ā
CORRECT VERSION:23void safeRecursion(int n, int limit) {24 printf("Call #%d\n", n);25 26 // BASE CASE: Stop when we reach the limit27 if (n >= limit) {28 printf("Base case reached! Stopping.\n");29 return;30 }31 32 // RECURSIVE CASE: Call with n+133 safeRecursion(n + 1, limit);34}3536int main() {37 printf("Safe recursion with base case:\n");38 safeRecursion(1, 5);39 return 0;40}Safe recursion with base case:
Call #1
Call #2
Call #3
Call #4
Call #5
Base case reached! Stopping.
06Types of Recursion
| Type | Description | Example |
|---|---|---|
| Direct Recursion | Function calls itself directly | factorial(n-1) |
| Indirect Recursion | A calls B, B calls A | funcA() ā funcB() ā funcA() |
| Tail Recursion | Recursive call is the LAST operation | return factorial(n-1, acc*n) |
| Head Recursion | Recursive call is the FIRST operation | factorial(n-1); then process |
| Tree Recursion | Function makes MULTIPLE recursive calls | fib(n-1) + fib(n-2) |
š Tail recursion example ā the recursive call is the last thing the function does.
1#include <stdio.h>23// ========== TAIL RECURSION ==========4// The recursive call is the LAST operation5// Can be optimized by compilers (uses less stack)67int factorial_tail(int n, int accumulator) {8 if (n <= 1) {9 return accumulator; // Base case10 }11 // Tail call - nothing to do after this returns12 return factorial_tail(n - 1, n * accumulator);13}1415// Wrapper function for easy use16int factorial(int n) {17 return factorial_tail(n, 1);18}1920// ========== TREE RECURSION ==========21// Function makes MULTIPLE recursive calls (like branches of a tree)22// Example: Fibonacci2324int fibonacci(int n) {25 if (n <= 1) {26 return n; // Base cases: F(0)=0, F(1)=127 }28 // TWO recursive calls - creates a tree!29 return fibonacci(n - 1) + fibonacci(n - 2);30}3132int 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}=== 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?
| Aspect | Recursion | Iteration (Loops) |
|---|---|---|
| Memory | Uses more (stack frames) | Uses less |
| Speed | Usually slower | Usually faster |
| Code Readability | More elegant for some problems | Can be complex for some problems |
| Risk | Stack overflow possible | No stack overflow |
| Best For | Trees, graphs, divide-conquer | Simple 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).
1#include <stdio.h>23// ========== RECURSIVE VERSION ==========4int factorial_recursive(int n) {5 if (n <= 1) return 1;6 return n * factorial_recursive(n - 1);7}89// ========== 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}1718int 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}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!