Recursion vs Iteration
Recursion — a function calling itself — is often the most natural way to express problems that are themselves recursively structured: traversing a tree, computing a factorial, or implementing divide-and-conquer algorithms like quicksort. But C makes no special accommodation for recursion the way some languages do, and understanding its real costs is essential before reaching for it.
The Cost of Recursion in C
Every call pushes a new stack frame -- return address, parameters, and local variables -- consuming real, limited stack memory
C compilers are not required to perform tail-call optimization, and most do not reliably apply it even when a function is technically written in tail-recursive form
Deep or unbounded recursion can exhaust the call stack, causing a stack overflow -- a crash with no graceful recovery in standard C
Each call carries real function-call overhead (pushing arguments, jumping, returning) that a loop does not pay
A Recursive Function
// Recursive: elegant, but one stack frame per element.
int sum_recursive(int *arr, int n) {
if (n == 0) {
return 0;
}
return arr[0] + sum_recursive(arr + 1, n - 1);
}For a large array, this pushes one stack frame per element — summing a million-element array this way risks overflowing the stack on many systems, even though the logic itself is perfectly correct.
Converting to Iteration
The simplest fix, when possible, is to rewrite the recursive structure as a plain loop with an accumulator — no stack growth at all, regardless of input size:
// Iterative: same result, constant stack usage.
int sum_iterative(int *arr, int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}// Recursive tree traversal (in-order print), for comparison:
void print_inorder_recursive(struct node *root) {
if (root == NULL) return;
print_inorder_recursive(root->left);
printf("%d ", root->value);
print_inorder_recursive(root->right);
}
// Same traversal, iterative, using an explicit stack of node pointers.
void print_inorder_iterative(struct node *root) {
struct node *stack[MAX_DEPTH];
int top = -1;
struct node *current = root;
while (current != NULL || top >= 0) {
while (current != NULL) {
stack[++top] = current;
current = current->left;
}
current = stack[top--];
printf("%d ", current->value);
current = current->right;
}
}The explicit-stack version does the same work but stores pending "come back to this node" state in an array you control (sized to a known bound, or heap-allocated and grown dynamically) rather than in call frames you do not control.
When to Prefer Each
Situation | Prefer |
|---|---|
Input size is small and bounded (e.g. a fixed-depth tree) | Recursion -- clearer, matches the problem structure |
Input size is large, unbounded, or attacker-controlled | Iteration -- avoids stack overflow risk entirely |
The algorithm is naturally divide-and-conquer (quicksort, mergesort) | Recursion -- the recursive structure is the algorithm |
Performance-critical hot loop | Iteration -- avoids per-call overhead, easier for the compiler to optimize |
Simple linear accumulation (sums, counts, searches) | Iteration -- a loop expresses the same logic with O(1) stack usage |