As you correctly interpret, the function Factorial
calls itself unless the argument is 0
. Since it calls itself with n - 1
, eventually the sequence of recursive calls will stop after n
steps. Each call is a new instance or invocation of the function, the computation is suspended until the new instance returns. The state of a given instance is kept in memory in an area called the stack (on most current environments).
When the final instance called with the value 0
returns, the last calling instance can finish its computation and return 1 * 1
to its caller, and so on until the initial instance can finish its multiplication and return the factorial of its argument.
------- stop reading here unless you are interested in implementation details --------
First problem with the above code, if n
is negative or very large, the program will likely invoke undefined behaviour because of too many recursive calls, each consuming some stack space, a limited resource.
Second problem: the int
type has a limited range of possible values. On most curent systems, int
is stored as 32 bits. Factorials grow very quickly, so Factorial(12)
yields 479001600
, which causes arithmetic overflow when multiplied by 13
because the result 6227020800
cannot fit in 32 bits. Arithmetic overflow invokes undefined behaviour.
You can try and compile your Factorial
function on this site: http://gcc.godbolt.org/# . It has an interactive compiler and shows the assembly code. Try different optimizer options:
-m32 -O1
will give a recursive version, not too difficult to understand
-m32 -O2
shows that the compiler is astute enough to transform your recursive C implementation to an iterative assembly version, both shorter and faster than the -O1
version.
-m32 -O3
yields vastly more complex code, involving SIMD instructions on MMX registers... potentially faster than the -O2
version, but completely over-engineered since a simple small lookup table without a single test would suffice:
int Factorial(int n) {
static int f32[16] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320,
362880, 3628800, 39916800, 479001600,
0, 0, 0 };
return f32[n & 15];
}
Notice how one can take advantage of undefined behaviour semantics (anything goes) to simplify the implementation and remove tests. Compilers are allowed to do that and some of them do indeed.