Testing and Debugging

Objective

As your knowledge of the C++ language grows, your capacity to write more useful and complex programs also increases. The if statement is a powerful tool for decision making. However, it can lead to considerable confusion if you do not fully understand its usage. This laboratory familiarizes you with the if statement, Boolean logic, and program decision making.

Key Concepts

Getting Started

This lab walks you through a variety of exercises, asking you questions along the way. We are providing you with a file answerFrom.txt which you should edit when entering your answers to such questions. To complete the lab, please submit this file of answers electronically through the course web page.

The files you need can either be downloaded via a web browser or copied to your account directly on turing by using the following command from your own directory:

cp -Rp ~goldwasser/csp125/labs/lab05 .

Syntax Errors Aren't the Only Problem

There are two kinds of errors - syntax and logic. Code often compiles with no difficulty (i.e., there are no syntax errors). Does this mean that the code will work? Well, it will do exactly what you told it to do, but not necessarily what you want it to do. Careless programmers write programs that they think look correct and that compile just fine, but do not run properly (i.e., there are logic errors). This situation results in frustrated users.

Besides carelessness, another source of programmer logic errors is misunderstandings about the uses of particular language features and their effects on the control of the flow of execution in the program (what the program does and where it goes when you run it).

The if statement allows the programmer to make decisions. If some expression is true, one action can be taken. If the expression is false, another action can be executed. The if statement can definitely lead to problems. The following exercises will help you to better understand the if statement so that you can avoid some its more troublesome pitfalls.

Open the program triangle.cpp. This program expects three numbers in non-decreasing order as input. It first determines whether the user correctly entered the numbers. If the user did not, the program terminates. If the instructions were followed, the program determines whether the numbers correspond to the sides of a triangle, and if they do, what kind of triangle.

 

Ordering

Expected Result

Actual Result

5 4 9

 

 

4 5 9

 

 

 

Next use the if statement to help compute the value of a standard Boolean operation.

 

 

Tracking down problems in your code (The Debugger)

 

The debugger is a powerful utility to help you find software defects in your program it helps you “get the bugs out.”  Note: The debugger does NOT find errors, rather it is a tool to help you to track down problems with your code.

 

A debugger is a program that runs other programs, allowing the user to exercise control over these programs, and to examine variables when problems arise.  GDB has tons of features, however, you only need to use a few for it to be very helpful. There is complete documentation for GDB online, or you can read the man page, and the quick reference sheet is very handy.

Also, if you did not pick one up, this reference card is useful when first learning the gdb commands. It's in pdf, so you can print it out easily.

Basic features of a debugger

When you are execute a program that does not behave as you like, you need some way to step through you logic other than just looking at your code. Some things you want to know are:

Starting GDB

You need to tell the g++ compiler that you plan to debug your program. You use the -g flag to do this. The command will now look like  (where progname is the name of your program)

 

                       g++ -g –o progname progname.cpp 
 

which will create the progname executable. To run this under the control of gdb, you type

 
                       gdb progname

 

This starts up the text interface to the debugger.

GDB commands

When gdb starts, your program is not actually running. It won't run until you tell gdb how to run it. Whenever the prompt appears, you have all the commands on the quick reference sheet available to you.

Starts your program as if you had typed

./progname 

Creates a breakpoint; the program will halt when it gets there. The most common breakpoints are at the beginnings of functions, as in

(gdb) break Traverse

Breakpoint 2 at 0x2290: file main.c, line 20

The command break main stops at the beginning of execution. You can also set breakpoints at a particular line in a source file:

(gdb) break 20

Breakpoint 2 at 0x2290: file main.c, line 20

When you run your program and it hits a breakpoint, you'll get a message and prompt something like this.

Breakpoint1,Traverse(head=0x6110,NumNodes=4)at main.c:16

(gdb)

Removes breakpoint number N. Leave off N to remove all breakpoints. info break gives info about each breakpoint

Provides a brief description of a GDB command or topic. Plain help lists the possible topics

Executes the current line of the program and stops on the next statement to be executed

Like step, however, if the current line of the program contains a function call, it executes the function and stops at the next line. step would put you at the beginning of the function

Keeps doing nexts, without stepping, until reaching the end of the current function

Continues regular execution of the program until a breakpoint is hit or the program stops

Produces a backtrace - the chain of function calls that brought the program to its current place. The command backtrace is equivalent

prints the value of E in the current frame in the program, where E is a C expression (usually just a variable). display is similar, except every time you execute a next or step, it will print out the expression based on the new variable values

Leave GDB.

The goal of gdb is to give you enough information to pinpoint where your program crashes, and find the bad pointer that is the cause of the problem. Although the actual error probably occurred much earlier in the program, figuring out which variable is causing trouble is a big step in the right direction. Before you seek help from a TA or Instructor, you should try to figure out where your error is occurring

 

 

A useful feature the debugger offers is the Print option. By using print commands, you are able to observe the value of objects at different points in the program.

 

Another useful feature is the Breakpoint option. By inserting a breakpoint, you can run your program in the usual fashion (e.g., by choosing Run). When the program reaches the statement where you have inserted a breakpoint, it pauses its execution and awaits your commands. Breakpoints are useful because they enable you to run large programs until they reach the place in the code that you want to observe. Let’s insert a breakpoint into the nand.cpp code.

 

 

You should to run the program for all combinations of p and q. When prompted for values, enter the values in the table below *** You will need to record your answers to submit ***      

p

q

p nand q

0

0

 

0

1

 

1

0

 

1

1

 

 

 

 A SIMPLE PREDICTION

 

 

#include<iostream>

using namespace std;

 

int main()

{

        bool p;

        bool q;

        bool r;

 

        cout << "Enter 3 logical values (e.g., 0 1 0): ";

        cout << "Leave a space between each value";

        cin >> p >> q >> r;

        if (p && q)

        {

         if (r)

         {

          cout << "1" << endl;

         }

         else {

         cout << "2" << endl;

         }

        }

        else if (q && r) {

               cout << "3" << endl;

             }

             else {

                if (p || !r) {

                   cout << "4" << endl;

                }

                else {

                  cout << "5" << endl;

                }

             }

        return 0;

}

 

 

Open the file nested.cpp. Try your inputs. If they do not work, modify them. *** Record your successful inputs on your answer form ***

 

Desk Checking!

 

We all know that before we enter a program into our text editor that it should be read to check that it is reasonable and a desk-check carried out to verify its correctness. A programmer carries out a desk-check by entering a simple set of input values and checking that the correct result is produced by going through the program and executing each instruction themselves.

The following program, miles2km.cpp, converts miles to kilometers.  The calculation based on the rule that one mile is equivalent to 1.6 kilometers.

#include <iostream>

using namespace std;

 

int main()

{

    const double factor = 1.6;

    double miles,

           kmts;

    cout << endl;

    cout << "Enter number of miles: ";

    cin >> miles;

    kmts = factor/miles;  

    cout << endl;

    cout << miles << " miles are equivalent to "

   << kmts << " Kilometers";

    cout << endl;

 

    return 0;

}

The program runs, but gives the wrong output.    After visually inspecting and desk checking the program, identify the source of the program.  Use desk checking if you cannot find the error.  Modify the program to work correctly and ***copy it onto your answer form***

Finishing Up.

 

Ensure you have answered all the questions and submit all the completed answer form electronically.