Lab 3
Passing arrays to functions; Using a debugger; Recursive functions

Objectives

Note: don't worry if you can't finish the entire lab exercise. Use turnin (see step 8) to turn in as much as you've completed before you leave the lab. Make sure you finish the rest of the lab on your own time.

Background

The program gdb is a simple, command-line debugger that lets you look inside your program while it is running. For all but the simplest programs, a debugger is an essential tool that saves you many hours of development time and many frustrations about why something is not behaving the way you expect it to behave. For most modern projects, and in most professional situations, you would use a window-based debugger or an integrated development environment that has a debugger, editor, and other tools collected together in a coherent way. This lab teaches you the basic usage of a debugger in a command line environment.

What you should do...

  1. Sign the attendance sheet.

  2. Write a program which asks the user to enter an integer in the range
     0 < n <= 20
    The program then reads in n integers and saves them in an array. You may assume that the number of elements in the array will not exceed 20. Name your program lab3.c.

  3. Compile and run your program. Your program doesn't produce any output, so it's hard to tell if it works correctly!

  4. Use a debugger to convince yourself that the program works. The program gdb (gnu debugger) lets you run your program in a controlled way, one command at a time, so you can see exactly what's going on. Here are some useful gdb commands:
            help                    help
            list                    display ten more lines
            list  <line number>     display ten lines centered around 
                                    <line number>
            list -                  display ten lines previous to the lines
                                    just printed 
            print <variable name>   print the value of a variable
            break <function name>   set a breakpoint at the start of a function
            break <line number>     set a breakpoint at the specified line
            cont                    continue execution after a breakpoint
            clear <function name>   remove all breakpoints from function
            run                     run program with optional arguments
            step                    resume execution for just a single statement
            quit                    exit the debugger
            backtrace               shows layers of functions you are into
    
    A breakpoint is just what the name implies - a point at which you break out of the currently-executing program and are brought into the debugger. You can then check out the state of various variables to make sure they contain the values they should contain at that point. If you reach a breakpoint and you find something amiss, you know that there is a bug in your program in one of the instructions that executed prior to the breakpoint. If, on the other hand, everything looks OK, you can narrow down your search for bugs in the instructions that execute after the breakpoint. Loops are usually good places to put breakpoints; start by placing a breakpoint immediately before a loop and immediately after. If it looks like there may be a problem inside a loop, placing a breakpoint within the loop allows you to stop after each iteration and make sure the loop control variables and other variables contain the correct values.

    When you want to use the debugger, you must first tell the compiler to prepare the program for debugging. So recompile your program with the command gcc -Wall -g lab3.c. The -g switch tells the compiler to add information about line numbers, variable names and locations, and other stuff to the object file so that the debugger can find its way around the program. Start the debugger by typing gdb a.out. You'll see a welcoming message followed by the debugger's prompt:

    (gdb)
    
  5. Type the following command:

    list

    This will display 10 lines around the next line to be executed, in this case, the function main..

    list 4, 8

    will list lines 4-8 inclusive. You can use the command help list to discover more listing options. Now run your program by typing the command:

    run

    You will see that the program runs normally, just as if you had tried to run it in a command shell. Type help run for more information.

    Set a breakpoint at your return 0 statement. To do this, type

    break x

    where x is the line number of your return statement. Use run to execute your program again. This time execution stops at the breakpoint. Now that the program is suspended, you can poke around your program to make sure everything looks OK.

    Use the print command to verify that the array contains the values you expect it to contain. If your array name is myArray, you can display the array's values this way:

    print myArray

    or, to display a single array item,

    print myArray[0]

    Finally, continue the execution of your program after the breakpoint with the continue command:

    cont

  6. Now that you're sure your program runs correctly, add a function called largestItem that returns the largest number stored in the array. Be sure to pass the array as a parameter to the function. You should also pass the current size of the array as a parameter. (Hint: start by assuming that the first item in the array is the largest one. Then step through the array elements one at a time, comparing each element with the largest element encountered so far, and updating the largest element seen so far if necessary.) Compile your program. Running your program with gdb, set a breakpoint using the command

    break largestItem

    This will cause the program to be suspended whenever the function largestItem is called. Use run to run your program. When the program stops at the breakpoint, single-step through the next line by typing

    step

    This causes execution for just one instruction, and the next line to be executed is displayed (not the line you just executed). Use step a few more times, verifying the contents of variables with the print command as you go. It can take a lot of stepping to get through a loop; set a breakpoint at the return statement in your function, and cont execution. Convince yourself that your program is working correctly, making multiple runs on various sets of input. If you are running out of time, go to step 8. If you have at least 15 minutes left, proceed to step 7.

  7. (We haven't gone over recursive functions in lecture yet, but you are welcome to try this out. Think about how we constructed recursive functions in Racket, and see if you can apply those same principles to a recursive function in C.) Now write a second version of the same function, but this time write the function recursively. What is the "size" of this problem? How can you reduce the size with each successive recursive call? What would the base case be for this function, and what would it return? Compile your program. Use gdb to verify that your program works. How efficient is the recursive version of the function as compared to the iterative version?

  8. Turn in your file using web-based turnin. The name of the turnin project is Lab 3.

  9. Refer to this lab page as you work on the remaining assignments this term. The program you examined with the debugger today was fairly simple. You may be thinking that you could more easily debug this program just by looking at it. As your programs become more complicated and harder to debug, you will appreciate the value of a tool like gdb. If you plan to take additional courses in Computer Science, debugging is a skill you must have. Type man gdb at the Linux prompt if you would like more information about gdb.

See you next week!