Download lab5.tar.gz and untar it (I use the command tar -xvzf lab5.tar.gz.) This makes a new lab5 subdirectory of your current directory. There should be 3 files in the lab5 subdirectory, one called Makefile, a fact.c file and an ackerman.c file
The Makefile contains targets for each these two programs I have provided, fact and ackerman, as well as a test target that excercises both of these program. Run make test to try these out.
We have already worked some with factorials in lab 2. Today, I have added some print statements to help clarify what is going on in recursion. If you look at the fact.c function, you will notice that it prints a message out when it is first called, and another message just before it returns an answer. The recursive call to the fact function comes in between the two print statements.
Furthermore, I've added a "prefix" argument to the fact function. The prefix function keeps track of how deeply we are nested in recursion. Every time we call the fact function recursively, I have created a new prefix called "newPref" which indents by three spaces, and pass that new prefix into the recursive call.
Think about function calls and local variables. A pointer to the "pref" parameter is passed in to the function. When we call "fact" from the "main" function, we use the constant empty string, denoted by "" as the "pref" parameter. This causes C to create an empty string in memory (an empty string has one character... the null terminator), and pass that into the top level invocation of "fact". Then "fact" creates a local variable called "newPref" that is a character array that is 256 characters long, initialized to a string of three blanks (plus the null terminator character). Then "fact" uses the "strcat" function to add the prefix that was passed in as a parameter to the newPref array. Note that "strcat" assumes there is enough room in newPref for both its original value AND the prefix that was passed into fact as an argument. Then, fact passes a pointer to the beginning of the newPref character array when it invokes "fact" recursively.
Keep in mind that the RECURSIVE call to fact starts and ends before the CALLERs version of "newPref" goes away. The "newPref" variable goes away when the higher level of fact returns. Therefore, it is perfectly valid in this case to pass a pointer to a local variable as an argument!
There is a danger here... namely that if we recurse too deeply, then we overflow the 256 byte size of newPref. (In fact, we overflow the size of an integer and get the wrong answer long before we overflow the 256 newPref size, but you can run fact with various different parameters how big you can get and what happens when newPref overflows.
But what is really important here is to see how we run fact up to the recursive call, then run one lower level of fact until we get to the recursive call, and so on, until finally we get to the base call which no longer requires recursion... fact(2) in this case. Then we run the back end of the fact function, which returns to its caller and runs the back end, and so on, until we get back to the top level.
But factorial is a really simple recursion... let's look at a couple of slightly more complicated recursive functions.
Fibonacci numbers are fascinating, and go back a long long way. For a complete description see the Wikipedia article on Fibonacci Numers. The basic description is fib(n)=n for n<3, fib(n)=fib(n-1) + fib(n-2) for n>=3. Copy the fact.c file as "fib.c", and change the fact function to a fib function. Leave in the prints and the prefixes so we can see how the recursion works, but change what gets printed to match the fib function. Add "fib" to your Makefile, and test fib 10. Notice that since the Fibonacci function invokes itself recursively twice, the pattern gets more complicated.
To get a feel for just how complicated recursion can get, take a look at the ackerman.c file - specifically at the "ack" function. The ack function takes two parameters, m and n, and returns a very simple recursively caclulated result. In fact, the definition in C takes only three lines. I've left out the printf statements that show the recursion though, because it gets to be too much information. Notice that if you run ackerman 3 2, you get an answer fairly quickly. To learn more about the ackerman function, see the Wikipedia article on Ackermann.
Now, run ./ackerman 4 2. This runs for a long time ... so long I eventually gave up and hit Ctrl-C to interrupt it. To get a feel for what is going on, start up the ackerman function in gdb by typing gbd ackerman. Then at the gbb prompt, type run 4 2. After you have run for a while, hit Ctrl-C to interrupt this function. Then, type where. Scroll through a few pages and look at the parameters passed into the ack function... and that's just with a value of m=4! Eventually, you will use up all of the available memory on the machine and get a segmentation violation. (In the old days, this happened much quicker, but these days the machines have too much memory to demonstrate exactly what is going on!)
Take a break this week! No lab report required.