Reputation: 537
I have the following code:
#include <stdio.h>
void SingSongFor(int numberOfBottles){
if (numberOfBottles == 0){
printf("There are simply no more bottles of beer on the wall.\n\n");
} else {
printf("%d bottles of beer on the wall. %d bottles of beer.\n", numberOfBottles, numberOfBottles);
int oneFewer = numberOfBottles - 1;
printf("Take one down, pass it around, %d bottles of beer on the wall.\n\n", oneFewer);
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
}
}
int main(int argc, const char * argv[]) {
// We could sing 99 verses, but 4 is easier to think about
SingSongFor(4);
return 0;
}
According to my understanding the program must terminate after it prints:
There are simply no more bottles of beer on the wall.
But how come it resumes to print:
Put a bottle in the recycling, 1 empty bottles in the bin.
Put a bottle in the recycling, 2 empty bottles in the bin.
Put a bottle in the recycling, 3 empty bottles in the bin.
Put a bottle in the recycling, 4 empty bottles in the bin.
The if functions already printed out a message, but instead of terminating it gets to the else as well. How is this possible? And How is there a increment from 1 to 4 in "numberOfBottles"?
Update : This is my understanding of the code. Please correct me if I'm wrong.
Upvotes: 13
Views: 1229
Reputation: 31285
In your code:
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
The call to the SingSongFor
function does not return until it has completed (and all the child functions which it called have also completed).
So the printf
line is forced to wait until all the lower SingSongFor
calls have finished, before it will run.
In other words, calling a function does not queue it up for execution in future, it runs that function immediately, and execution of the current function is paused until the called function has finished.
This process is why function calls create a stack of execution states, with each stack entry holding the local values that will be needed when execution at that level resumes (in this case, just the numberOfBottles
variable, and the memory address of the next instruction to that is waiting to run).
This pushing and popping of stack entries can make recursion (briefly) consume a lot of memory, and in the worse case, when the stack fills up too much memory, this can result in ... a stack overflow!
Upvotes: 1
Reputation: 84561
If I understand you only want to print the total in the recycle bin once when the recursion completes, then the easiest solution is to use a wrapper
(or helper) function to call SingSonfFor
. By using a wrapper, you preserve the original numberOfBottles
as the count for the recycle, while each bit of recursion will reduce the number by one. Example:
#include <stdio.h>
#include <stdlib.h>
void SingSongFor (int numberOfBottles){
if (!numberOfBottles)
return;
int oneFewer = numberOfBottles - 1;
printf (" %d bottles of beer on the wall. %d bottles of beer.\n",
numberOfBottles, numberOfBottles);
printf (" Take one down, pass it around, %d bottles of beer on the wall.\n\n",
oneFewer);
if ((oneFewer) == 0)
printf (" There are simply no more bottles of beer on the wall.\n\n");
else
SingSongFor (oneFewer); // This function calls itself!
}
void ss_helper (int numberOfBottles)
{
SingSongFor (numberOfBottles);
/* Print a message just before the function ends */
printf(" Put a bottle in the recycling, %d empty bottles in the bin.\n",
numberOfBottles);
if (numberOfBottles >= 6)
printf ("\n Now go sober up you lush...\n\n");
}
int main(int argc, const char *argv[]) {
// We could sing 99 verses, but 4 is easier to think about
int coldbeers = (argc > 1) ? atoi (argv[1]) : 4;
// SingSongFor (coldbeers);
ss_helper (coldbeers);
return 0;
}
output:
$ ./bin/beersong
4 bottles of beer on the wall. 4 bottles of beer.
Take one down, pass it around, 3 bottles of beer on the wall.
3 bottles of beer on the wall. 3 bottles of beer.
Take one down, pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall. 2 bottles of beer.
Take one down, pass it around, 1 bottles of beer on the wall.
1 bottles of beer on the wall. 1 bottles of beer.
Take one down, pass it around, 0 bottles of beer on the wall.
There are simply no more bottles of beer on the wall.
Put a bottle in the recycling, 4 empty bottles in the bin.
Upvotes: 2
Reputation: 2664
The reason it's derpy is because you're calling the next function BEFORE you print the number of bottles in the current function. So when it all catches up, the last function prints first.
Currently:
SingSongFor(oneFewer); // This function calls itself!
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
What you Want:
// Print a message just before the function ends
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
SingSongFor(oneFewer); // This function calls itself!
That way, you'll get the output you expect and THEN the next step-down happens.
Upvotes: 3
Reputation: 532465
To understand why your program acts as it does, it's necessary to understand how a function call works. The fact that it's recursive might enable the compiler to make some optimizations that improve the efficiency of the program, but conceptually being recursive doesn't really change what is happening.
First, let's examine an alternative program that does basically the same thing as your program, using non-recursive function calls.
void SingSongFor4(){
printf("4 bottles of beer on the wall. 4 bottles of beer.\n");
printf("Take one down, pass it around, 3 bottles of beer on the wall.\n\n");
SingSongFor3();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 4 empty bottles in the bin.\n");
}
}
void SingSongFor3(){
printf("3 bottles of beer on the wall. 3 bottles of beer.\n");
printf("Take one down, pass it around, 2 bottles of beer on the wall.\n\n");
SingSongFor2();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 3 empty bottles in the bin.\n");
}
}
void SingSongFor2(){
printf("2 bottles of beer on the wall. 2 bottles of beer.\n");
printf("Take one down, pass it around, 1 bottles of beer on the wall.\n\n");
SingSongFor1();
// Print a message just before the function ends
printf("Put a bottle in the recycling, 2 empty bottles in the bin.\n");
}
}
void SingSongFor1(){
printf("1 bottles of beer on the wall. 1 bottles of beer.\n");
printf("Take one down, pass it around, 0 bottles of beer on the wall.\n\n");
printf("Put a bottle in the recycling, 1 empty bottles in the bin.\n");
}
}
int main(int argc, const char * argv[]) {
// We could sing 99 verses, but 4 is easier to think about
SingSongFor4();
return 0;
}
I hope that it's obvious that each function prints a couple of lines, calls the next function, then prints another line. Each called function does this in turn so, for example, 2 lines for SingSongFor4()
are printed, then SingSongFor3
is called. This prints its 2 lines, then calls SingSongFor2()
, which prints its lines and so forth. SingSongFor1()
doesn't call any other functions so it prints all three lines then returns to SingSongFor2()
which completes and so on up the chain. In all you get the 8 lines of X bottles on the wall/take one down
as you follow the function calls "down", then the 4 lines of "Put a bottle in the bin" as you return "up" the reverse direction.
Your function isn't any different except that it's been parameterized and had a little logic added to determine when it should act like SingSongFor1()
and when it should act like the other 3. I say it's not different except that in your case you have a single copy of the text of the program that's shared by each invocation of the program rather than 4 separate (nearly identical) copies of the text. What makes it possible to share a copy of the text is the local context of each function call - the parameters, variables, and some housekeeping information about where the program lives and the state of execution of the program.
Typically this context information is contained in a special data structure called the stack. It's called a stack because you stack things one on top of the other, then remove them one at a time from the "top." Each stack frame contains the context of one invocation of the function: the parameters - in your case numberOfBottles
; the local variables - oneFewer
; and information about which statement should be executed when the function ends or returns. When a function is called the frame corresponding to that call is pushed onto the stack and the text of the function is executed. When it finishes, the frame is popped off and execution resumes in the calling function at the point where it left off (which was stored in the popped off stack frame for this purpose). It resumes using the new "top" frame of the stack for it's context.
The important thing, though, is that your recursive function works exactly the same way as any other function - each time it's called it gets a new context of its very own, even though the text of the function is the same. It executes to completion then returns to the previous context - which may have been the same function, though with a different context.
Upvotes: 21
Reputation: 3850
Everything is just right. When you reach the final message There are simply no more bottles of beer on the wall.
your program returns to the point where it was called (it was called in function SingSongFor
with argument 1
). Then prints the message Put a bottle in the recycling, 1 empty bottles in the bin.
and returns to previous call in function SingSongFor
with argument 2
. And like this up to 4.
Upvotes: 3
Reputation: 409176
The recursive function calls are stacked. So it goes something like this:
SingSongFor(4) | v SingSongFor(3) | v SingSongFor(2) | v SingSongFor(1) | v SingSongFor(0)
The last call prints that there are no more bottles and then returns and this is there the magic happens: It returns to the previous call, which will then print the message about the recycling bin, and return to the previous call which again print its message, and so on.
Upvotes: 6
Reputation: 542
3 bottles:
SingSong(3):
PRINT 2 LINES
SingSong(2):
PRINT 2 LINES
SingSong(1):
PRINT 2 LINES
SingSong(0):
PRINT 1 LINES
PRINT RECYCLE LINE
PRINT RECYCLE LINE
PRINT RECYCLE LINE
After your final inner recursive call happens, it then backs its way out through each method call and calls the recycling message.
Upvotes: 20
Reputation: 59997
After the line
SingSongFor(oneFewer); // This function calls itself
You have a printf i.e.
printf("Put a bottle in the recycling, %d empty bottles in the bin.\n",numberOfBottles);
So that is what the program does. With the value of numberOfBottles
that is stored on the stack.
Upvotes: 4