A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
Direct recursion is easy to spot: function A calls function A. Indirect recursion is sneakier: function A calls function B, which calls function C, which calls function A. The cycle involves multiple functions. You will not find it by searching for a function name in its own body. You find it by tracing the call graph. Indirect recursion appears in state machines, mutual parsers, and event dispatch systems, and it requires forward declarations to compile.
#include <stdio.h>void countdown(int n) {if (n <= 0) {printf("Go!\n");return;}printf("%d\n", n);countdown(n - 1);}int main(void) {countdown(3);return 0;}
#include <stdio.h>void is_even(int n);void is_odd(int n);void is_even(int n) {if (n == 0) {printf("0 is even.\n");} else {is_odd(n - 1);}}void is_odd(int n) {if (n == 0) {printf("0 is odd? No. Reached base via odd.\n");} else {is_even(n - 1);}}int main(void) {is_even(7);is_even(4);return 0;}
is_even(7) calls is_odd(6), which calls is_even(5), which calls is_odd(4), and so on. The recursion bounces between two functions. Neither function calls itself directly. The forward declarations at the top are mandatory because is_even references is_odd before its definition appears. Without them, the compiler has not seen is_odd when it processes is_even's body.
Recursive descent parsers use it constantly. An expression parser calls a term parser, which calls a factor parser, which might call back into the expression parser for parenthesized sub-expressions. The call cycle is structural. It mirrors the grammar. Eliminating the recursion would mean rewriting the parser as a state machine, which is possible but far less readable.
When debugging an indirect recursion stack overflow, the stack trace shows a repeating pattern of multiple function names rather than one name repeated. Look for cycles in the trace: A → B → C → A → B → C. That pattern tells you the recursion is mutual, not self-referential.
Indirect recursion is not a curiosity. It is the backbone of parsers, interpreters, and event systems. The debugging challenge is recognizing the cycle in a stack trace. The compilation challenge is forward declarations. The design challenge is ensuring the cycle converges. Next: head vs tail recursion, and why the position of the recursive call matters for optimization.
Finish the lesson once you have worked through the material. This awards ★ 30 XP.