Skip to content

C Recursion

Recursion is a programming technique where a function calls itself, either directly or indirectly, to solve a problem. It breaks down a problem into smaller, self-similar subproblems until it reaches a simple base case that can be solved directly.

A recursive function typically has two main parts:

  • Base Case(s): One or more conditions that stop the recursion. When a base case is met, the function returns a value directly without making another recursive call. This is crucial to prevent infinite loops.
  • Recursive Step: The part where the function calls itself with modified arguments, moving closer to the base case. The result of the recursive call is typically used to calculate the result for the current call.

Conceptual Example:

void recursive_function(parameters) {
if (base_case_is_met) {
// Solve the simplest case directly
return base_case_value;
} else {
// Recursive step: Call itself with modified parameters
// Process the result of the recursive call
// Return the final result for this step
return recursive_call_result_processed;
}
}

While C supports recursion, you must ensure a base case exists and is eventually reached; otherwise, the program will likely run out of stack memory (stack overflow) and crash.

Recursion is often elegant for problems that have a naturally recursive structure, like traversing trees or solving certain mathematical problems.

The factorial of a non-negative integer n (denoted n!) is the product of all positive integers less than or equal to n. 0! = 1.

#include <stdio.h>
#include <stdlib.h> // For EXIT_SUCCESS
// Use unsigned long long to handle larger factorial values
// Factorials grow very quickly!
unsigned long long factorial(unsigned int n) {
// Base case: factorial of 0 is 1
if (n == 0) {
return 1;
}
// Recursive step: n * factorial(n-1)
else {
// Check for potential overflow before multiplication, though tricky.
// A full check is complex. For large n, this might still overflow.
return (unsigned long long)n * factorial(n - 1);
}
}
int main(void) {
unsigned int num = 15;
// Note: factorial(15) fits in unsigned long long, but factorial(21) would overflow.
printf("Factorial of %u is %llu\n", num, factorial(num));
// Use %u for unsigned int, %llu for unsigned long long
return EXIT_SUCCESS;
}

When compiled and executed, this produces:

Factorial of 15 is 1307674368000

The Fibonacci sequence starts with 0 and 1. Each subsequent number is the sum of the two preceding ones (0, 1, 1, 2, 3, 5, 8…).

#include <stdio.h>
#include <stdlib.h> // For EXIT_SUCCESS
// Calculates the nth Fibonacci number (using recursion)
unsigned long long fibonacci(unsigned int n) {
// Base cases
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
// Recursive step
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main(void) {
int i;
int limit = 10; // Calculate first 10 Fibonacci numbers (0 to 9)
printf("First %d Fibonacci numbers:\n", limit);
for (i = 0; i < limit; i++) {
// Print the number, followed by a space
// The original tutorial's %n format specifier was incorrect and dangerous.
printf("%llu ", fibonacci(i));
}
printf("\n");
return EXIT_SUCCESS;
}

When compiled and executed, this produces:

First 10 Fibonacci numbers:
0 1 1 2 3 5 8 13 21 34

While the recursive Fibonacci solution is elegant, it is extremely inefficient. It recalculates the same Fibonacci numbers multiple times, leading to exponential time complexity (roughly O(2^n)). Calculating fibonacci(40) can take noticeable time.

For practical applications, iterative solutions (using loops) or memoization/dynamic programming techniques are vastly superior for calculating Fibonacci numbers.

Every recursive problem can also be solved iteratively using loops (like for or while).

  • Recursion: Can be more intuitive for problems with self-similar structures. Often leads to shorter, more readable code (for some problems). Can consume significant stack space due to nested function calls, risking stack overflow for deep recursion. Can be less efficient due to function call overhead and repeated calculations (like the naive Fibonacci example).
  • Iteration: Generally more efficient in terms of speed and memory usage (uses heap or fixed stack space, not growing call stack). Can sometimes be harder to conceptualize or write for complex recursive structures. Avoids stack overflow risk associated with deep recursion.

Choose recursion when it significantly simplifies the logic and the recursion depth is expected to be manageable. Otherwise, prefer iteration for better performance and memory safety.