I wrote a strange function to find the factorial of a number
int strange_fact(int n=0)
{
static int i=n;
static int j=i;
if(j>1)
{
i *= --j;
strange_fact();
return 0x7777; //<------ This line
}
else
return i;
}
When I commented the 9th line, I was getting the expected output. But I encountered a strange (or maybe not so strange) behaviour after adding that line. What happens when I uncomment it is that program flow reaches line 9 even though a recursive function call precedes it. My question is, how does flow reach line 9?
When recursive call to function ends, line 9 will be reached. See this (shorter) example:
int foo(int i) {
if(i > 0) {
foo(i-1);
return 0x7777;
} else {
return i;
}
}
So when calling foo(1) it will go through first if (because 1 > 0) and foo(0) will be called. Now inside this call (foo(0)) program will go into else barnch (because 0 is not > 0) and foo(0) will return 0. So now we will be back to our first call (foo(1)) and as foo(0) returned, foo(1) will return 0x7777.
This line
strange_fact();
does the recursion and throws away the result.
The next next line
return 0x7777;
will return that value finally.
If you remove that line and compiled with the warnings on it will inform you that all paths do not return a value
You need to understand how recursions work.
After calling strange_fact() you've put a return statement. Meaning once the function is executed, it's still going to return 0x7777, which screws the answer up.
Check this out, this should work as expected:
int strange_fact(int n=0)
{
static int i=n;
static int j=i;
if(j>1)
{
i *= --j;
strange_fact();
// return 0x7777; // We don't want to return a value yet
}
if(j<=1) // We need to check this condition after strange_fact is executed
return i;
// This line will never be executed
return 0x7777; //<------ This line
}
If you eliminate the static variables, you'll get something like this:
long fact(long i)
{
if(i > 0)
return i * fact(i-1);
if(i == 0) // factorial(0) = 1
return 1;
throw exception("Negative numbers cannot have factorials"); // Or you can handle it by returning a -ve error code
}
The whole thing, explained by a diagram
Let us consider the case of strange_fact(3)
So, whatever happens in the recursive steps becomes meaningless because ultimately, 0x7777 will be returned when the control comes back to the first call.
Related
I think that following two codes are identical. but upper one has C4715 "not all control paths return a value" problem and other doesn't.
Why this warning happens?
int func(int n){
for(int i=0; i<1; i++){
if(n == 0){
return 0;
}
else {
return -1;
}
}
}
int func(int n){
if(n == 0){
return 0;
}
else {
return -1;
}
}
The compiler is trying to be helpful, and failing. Pretend for a moment that the code inside the loop was just if (n == 0) return 0;. Clearly, when n is not 0, the loop will execute once and then execution will move on to the next statement after the loop. There's no return statement there, and that's what the compiler is warning you about. It just isn't smart enough to see that the code inside the loop always returns.
So, possibly, add a return 0; statement after the loop. That might make the compiler happy. But it also might make the compiler (or some other compiler) give a different warning about "unreachable code", because that new return statement can't actually be reached. This stuff is hard to analyze, and compilers often get it wrong.
Because warnings don't promise to only flag incorrect code. Nor do they promise to flag all incorrect code. It's not possible to be completely accurate.
It seems like the part of the compiler that issues C4715 assumes that a for loop with an end condition ends in some cases, and doesn't try to calculate if it will always return early.
My first thought is that the loop cannot act.
You initialize the for loop with int i = 0 then give bounds of i < 1 with an action if i++
Given i is type int with step of 1, it can never loop.
If I put a break point on this function it will step through until the base case is met but when it hits the return 1 it doesn't actually exit the loop. Instead it goes to the bottom bracket and then bounces to int t = expo(m, n / 2) and steps downs to return t * t*m. It then goes back to the bottom bracket and repeats this process before eventually stopping. Can someone explain what is going on?
int expo(const int m, const unsigned int n) {
funcCallCounter++; //counts how many times the function is called
if (n == 0) {
return 1;
}
else {
if (n % 2 == 0) {
int t = expo(m, n / 2);
return t * t;
}
else {
int t = expo(m, n / 2);
return t * t*m;
}
}
}
When the flow of your program in a function encounters a return statement, control is returned to place that called that function. That is true even if it was the same function. This is completely normal and expected.
In your case, each call still has work to do after the recursive step (because your expo() call is never quite the last thing before return) and you're seeing the program get on with that work.
Keep an eye on your stack frame while debugging; you'll see that it's not really jumping around in the way that you think it is; you're returning to previous call contexts.
I thought up to this point in time, that I understood how a return works, but once I got into the recursions, I suppose I'm a bit more lost than the originally thought.
Suppose, I have a function for count, that how many times a char pops up in a string.
int frequency(char ch, string input, int pos) {
if (pos == inputString.length()) {
return 0;
}
if (inputString[pos] == ch) {
return 1 + frequency(ch, inputString, pos + 1);
}
else {
return frequency(ch, inputString, pos+1);
}
}
If I were to pass to it, the string "Jeff" and looking for "f", it returns a value of 2.
So, how does it know when to stop?
Does return 0 end any method with return type int?
And if so, why does it still return the value of 2, when the final return says return 0?
The last return
return 0;
is only the last time the function is called during the recursion. This is needed to stop the recursion at some point. For the call before this last one one of the other return statements is executed, e.g:
return 1 + frequency(ch, inputString, pos + 1);
Thus the 0 is added up to the 1 and any previous results of the recursion.
PS:
As long as the function return statement calls the function again the recursion continues. Only when the return simply returns something (without calling the fucntion again) the recursion stops.
Here is a more simple example that calculates the sum of all integers up to N:
int calcSum(int N){
if ( N == 1 ) return 1; // recursion stops here
return N + calcSum( N-1 ); // otherwise continue to add up
}
Multiple return statements in one function are not special to recursion. The function just returns on the first return it encounters.
So, how does it know when to stop?
When no more recursive calls are added from a particular branch in the recursively called function it will stop, and the call stack will be cleared with returning values in the reverse order of the calls were issued (LIFO). That's done here:
if (pos == inputString.length()) {
return 0;
}
Any of the other branches call the function recursively and take a step down in the call stack:
if (inputString[pos] == ch) {
return 1 + frequency(ch, inputString, pos + 1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
else {
return frequency(ch, inputString, pos+1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Does return 0; end any method with return type int?
Yes, and it will do for any return types that can be initialized with 0
And if so, why does it still return the value of 2, when the final return says return 0?
Because the results of the recursive call results were accumulated on the stack:
return 1 + frequency(ch, inputString, pos + 1);
// ^ the result of the operation will be saved on the stack when the call returns
... and you see the final result of the (first) recursive call in your driver function.
BTW, the much cheaper implementation by means of performance and memory usage would be a simple loop. There aren't any drawbacks regarding the linear time behavior anyway:
int frequency(char ch, string input) {
int result = 0;
for(int pos = 0; pos < input.size(); ++pos) {
if (input[pos] == ch) {
++result;
}
}
return result;
}
Think of a recursive calls as a stack of function calls. At some point it may hit return 0; This mean that one of the function calls on the stack is completed. Thus an element on the stack is popped. The function's final return happens when the stack is empty.
So, i have a class called Vuelo, it has a method in which i can add a passenger to an airplane flight, i must check that the passenger id is not already in the array (the array is at first with all zeros), i must also check that there is enough space for another passenger to be added (max 10)
bool Vuelo :: agregarPasajero(int id)
{
int i = 0;
for(int iC = 0; iC < 10; iC++)
{
if (listaPasajeros[iC] == id)
{
i++;
return false;
}
}
if(i == 0)
{
if(cantidadPasajeros >= 10)
{
return false;
}
else
{
return true;
cantidadPasajeros++;
}
}
}
If i is not zero, you get to the end of the function without any kind of return statement. Since you declared the function to always return a bool, you should provide one for that case.
Now, you may know that i will never be zero at that spot, but the logic for that is fairly complex (I missed it on the first reading), and a compiler cannot be expected to realize that there is in fact no chance of control flow ever getting to the end of the function without encountering a return. In this case it's best to add a dummy return.
You can probably get away with not having a dummy return if you remove the bogus i == 0 test. i will necessarily always be zero at that point, since if it were ever increased, the function immediately returns false.
The statement cantidadPasajeros++; will never be executed since it is located after a return statement. Any halfway decent compiler also warns on that.
My C++ code looks like this:
int f(int i){
if (i > 0) return 1;
if (i == 0) return 0;
if (i < 0) return -1;
}
It's working but I still get:
Warning: No return, in function returning non-void
Even though it is obvious that all cases are covered. Is there any way to handle this in "proper" way?
The compiler doesn't grasp that the if conditions cover all possible conditions. Therefore, it thinks the execution flow can still fall through past all ifs.
Because either of these conditions assume the others to be false, you can write it like this:
int f(int i) {
if (i > 0) return 1;
else if (i == 0) return 0;
else return -1;
}
And because a return statement finally terminates a function, we can shorten it to this:
int f(int i) {
if (i > 0) return 1;
if (i == 0) return 0;
return -1;
}
Note the lack of the two elses.
Is there any way to handle this in "proper" way?
A simple fix is to get rid of the last if. Since the first two are either called or not the third case must be called if you get to it
int f(int i){
if (i > 0) return 1;
if (i == 0) return 0;
return -1;
}
The reason we have to do this is that the compiler cannot guarantee that your if statements will be called in every case. Since it reaches the end of the function and it might not have executed any of the if statements it issues the warning.
Just help the compiler to understand your code. Rewrite the function the following way
int f(int i){
if (i > 0) return 1;
else if (i == 0) return 0;
else return -1;
}
you could also write the function for example like
int f( int i )
{
return i == 0 ? 0 : ( i < 0 ? -1 : 1 );
}
Another way to write the function in one line is the following
int f( int i )
{
return ( i > 0 ) - ( i < 0 );
}
The compiler doesn't know that all options are covered, because in terms of your function's syntax there's nothing to suggest it.
A simplified example:
int f(int i)
if if if
(int > int), return (int == int), return (int < int), return
A clearer structure like if/else with a return in each yields an Abstract-Syntax-Tree which clearly shows there's a return in each case. Yours, however, is dependent on the evaluation of nodes in the AST, which isn't covered in the syntax check (by which you're being issued a warning).
Beyond pure syntax, if the compiler were to rely on "possible" evaluations as well in trying to figure out the behavior of your program, it would ultimately need to entangle itself and probably hitting the halting problem. Even if it managed to cover some cases, this would probably spawn more questions from users than it would answer, and also risk an entirely new level of bugs.