c-tail-call-optimization
Table of Contents
C tail call optimization
C tail call optimization (TCO) is a compiler optimization where a function's final call becomes a jump, reusing the same stack frame. Recursive functions calling themselves at the end convert from O(n) stack space to O(1). Optimizers enable TCO with -O2 or higher; not guaranteed.
Write tail-recursive functions to potentially benefit from TCO; don't depend on it for correctness.
Example
// compile: gcc -O2 -o tail_call tail_call.c // run: ./tail_call // description: tail call optimization for recursion #include <stdio.h> // Tail-recursive factorial (compiler may optimize) long factorial_tail(int n, long acc) { if (n <= 1) return acc; return factorial_tail(n - 1, acc * n); // Tail call } // Non-tail-recursive factorial (can't optimize) long factorial_normal(int n) { if (n <= 1) return 1; return n * factorial_normal(n - 1); // Not tail call } // Tail-recursive sum long sum_tail(int n, long acc) { if (n <= 0) return acc; return sum_tail(n - 1, acc + n); // Tail call } // Fibonacci tail-recursive (accumulator pattern) long fib_tail(int n, long a, long b) { if (n == 0) return a; if (n == 1) return b; return fib_tail(n - 1, b, a + b); // Tail call } int main() { printf("Factorial(10) = %ld\n", factorial_tail(10, 1)); printf("Sum(100) = %ld\n", sum_tail(100, 0)); printf("Fib(20) = %ld\n", fib_tail(20, 0, 1)); printf("Large factorial(1000) = %ld\n", factorial_tail(1000, 1)); return 0; }
Common patterns
Tail call definition:
- Function's last operation is a call
- Return value is the call's return
- No computation after the call
Compiler requirements:
- Not guaranteed (depends on compiler, optimization level)
- GCC/Clang: -O2 or higher usually enables
- MSVC:
/O2or higher - Embedded/debug builds may not optimize
Accumulator pattern:
- Pass intermediate result as parameter
- Converts non-tail recursion to tail
- Example: factorial_tail(n-1, acc*n) vs n * factorial(n-1)
Benefits:
- O(1) stack space instead of O(n)
- Can recurse deeply without stack overflow
- No speed penalty (jump replaces call)
When TCO matters:
- Very deep recursion (thousands+)
- Real-time code with limited stack
- Most normal code: TCO is bonus, not requirement
Limitations:
- Not guaranteed by C standard
- Function signature must allow accumulator
- Some languages guarantee TCO (Scheme); C doesn't
Debugging note:
- With TCO: stack trace shows one frame (optimized away)
- Without TCO: stack shows full chain
- Can make debugging harder
Alternatives:
- Convert to loop (portable, clear)
- Use explicit stack (if recursion needed)
- Don't over-optimize without profiling
c-tail-call-optimization.md · Last modified: by 127.0.0.1
