Other answerers are correct that there's some compiler magic that converts the tail recursion to iteration, though it depends on the optimization settings of the compiler. In gcc for instance, if we compile with gcc -S -O1 someFile.c
(given your code), we get the following generated assembly:
fun:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $0, %eax
call fun
leave
ret
.LFE2:
.size fun, .-fun
So you can see, it's still using call/leave/ret instructions to perform an actual function call, which will kill the process. Once you start optimizing further with gcc -S -O2 someFile.c
we start getting the magic:
fun:
.LFB24:
.p2align 4,,10
.p2align 3
.L2:
jmp .L2
.LFE24:
.size fun, .-fun
.p2align 4,,15
It depends on your compiler and your compiler settings, so it helps to be friends with them.