Question: I was writing factorial using tail recursion and I have a question here. My original function looks like this

**Code snippet A**

```
#include <stdio.h>

int main(void)
{
 int n = 0;

 printf("Enter number to find factorial of : ");
 scanf("%d",&n);

 printf("fact == %d\n",fun(n,1));

 return 0;
}

int fun(int n, int sofar)
{
 int ret = 0;

 if(n == 0)
  return sofar;

 ret = fun(n-1,sofar*n);

 return ret;
}

```

However, even if I do not use return, it still works. This does not make perfect sense since I am returning the value only in the base case. Say if n==5, then 120 would be returned at the base case. But what gets returned from 4th invocation back to the 3rd invocation cannot be predicted since we are not explicitly specifying any return unlike in Code snippet A.

**Code snippet B**

```
int fun(int n, int sofar)
{
 int ret = 0;

 if(n == 0)
  return sofar;

 ret = fun(n-1,sofar*n);
}

```

I am thinking the above works because of some kind of compiler optimization ? Because If I add a printf statement to Code snippet B, it does not work anymore.

**Code snippet C**

```
 int fun(int n, int sofar)
    {
     int ret = 0;

     if(n == 0)
      return sofar;

     ret = fun(n-1,sofar*n);

     printf("now it should not work\n");
    }

```

Probably the printf causes something to be removed from the stack? Please help me understand this.

Answer: 
Not returning a value from a fuction which should return a value is [undefined behavior](https://stackoverflow.com/a/9936084/121747).

If it works by any luck then it's not an optimization but just a coincidence given by how and where these automatic allocated values are stored.
The reason why it seems to work in B is because the return value is most likely passed in a register on your architecture. So returning through all the layers of recursion (or if the compiler optimized the whole thing into iteration) nothing touches that register and your code appears to work.

A different compiler might not allow this to happen or maybe the next version of your compiler will optimize this differently. In fact, the compiler can remove most of the function because it is allowed to assume that since undefined behavior can't happen it must mean that the part of the function where the undefined behavior seems to happen will never be reached and can be safely removed.