Sie sind auf Seite 1von 88

Lab Manual for Problem Solving, Abstraction, and Design Using C++, third edition

Lynn Lambert, updated by Richard Ross Christopher Newport University Newport News, Va June 8, 2000

Laboratory 1
1.1 Objective

Introduction to C++

In this lab, you will learn the form of a C++ program what the components of a C++ program are how to write comments about compiler directives how to trace programs

1.2

Correspondence to Text

The material in this lab best corresponds to sections 2.1-2.6 in the text.

1.3

Introduction

This lab course provides an introduction to computer programming using the C++ language. The version of C++ that will be used is called ANSI Standard C++, or just Standard C++. (ANSI is the name of the institute that developed the standard). In this lab, you will see your rst C++ program. You will also trace through a C++ program.

1.4

Examining a C++ program

Lets look at a C++ program. Copy intocm.cpp into your directory. Your instructor will tell you how to do that. Edit the le intocm.cpp. Your instructor will tell you the best way to edit your le. Note on Lab Manual format: I will use a bullet like this whenever there is something that you need to do on the computer. The le should look something like this: // // // // InchToCm This program inputs a real number inches and outputs its centimeter equivalent (also a real number)

#include <iostream> using namespace std; int main() { float centimeters, inches; cout << "This program converts inches to centimeters" << endl; cout << "Enter a number> "; cin >> inches; centimeters = inches * 2.54; cout << inches << " inches is equivalent to " << centimeters; << " centimeters" << endl; return 0; } // end main Lets examine this program a little more carefully. 1.4.1 Comments

The rst line of the le is: // InchToCm This line is a comment. Lets add a comment above the name of the program that contains your name. Edit your le. Add your name on the top line so that the rst two lines of the le now look like: 2

// your name // InchToCm Comments tell people reading the program something about the program. The compiler ignores these lines. 1.4.2 Compiler Directives

After the initial comments, you should see the following line: #include <iostream> This is called a compiler directive. It tells the compiler to do something. Compiler directives always start with a # sign. In this case, the compiler directive includes the information in the le iostream as part of your program. You will almost always have at least one include le in your program. These header les are stored in a library that we will learn more about in subsequent labs. (Note: Older C++ programs use a .h as part of the include le name, while Standard C++ include les have no extension, or .h). The next line, using namespace std;, tells the compiler that the objects that well be using are in the standard namespace. This line should be in all of your programs. Note that this line ends in a semicolon. 1.4.3 The function main

The next nonblank line int main () gives the name of a function. There must be exactly one function named main in each C++ program, and main is the function where program execution starts when the program begins running. The int indicates the function returns an integer. You will learn more about functions in the next two weeks, but essentially functions are units of C++ code that do a particular task. Large programs will have many functions just as large organizations have many functions. Small programs, like smaller organizations, have fewer functions. The parentheses following the words int main is a list of arguments to the function. In the case of this function, there are no arguments. Arguments to functions tell the function what objects to use in performing its task. The curly braces on the next line and on the last line of the program determine the beginning and ending of the function. 1.4.4 Variable Declarations

The line after the opening curly brace, float centimeters, inches; is called a variable declaration. This line tells the compiler to reserve two places in memory that are the size of a real number (thats what the oat indicates). The memory locations will have the names inches and centimeters associated with them. You will often have many dierent variables of many dierent types.

1.4.5

Executable Statements

Output and Input The statements following the variable declaration up to the close curly brace are executable statements. The executable statements are statements that will be executed when the program is run. cout and the output stream operator (<<) tell the compiler to generate instructions that will display information on the screen when the program is run, and cin reads information from the keyboard when the program is run. Assignment Statements The statement centimeters = inches * 2.54 is an assignment statement. It calculates what is on the right hand side of the equation (in this case inches * 2.54) and stores it in the memory location that has the name on the left hand side of the equation (in this case, centimeters). So centimeters = inches * 2.54 takes whatever was read into the memory location inches, multiplies it by 2.54, and stores the result in centimeters. The next statement outputs the result of the calculation. Notice that you do not need to put the entire statement on one line. That is, cout << inches << " inches is equivalent to " << centimeters << " centimeters" << endl; produces the same output when run as does cout << inches << " inches is equivalent to " << centimeters << "centimeters" << endl; So, how do we get the program to write a return character so that the lines when output dont all run together? The character that causes a return character to be output to the screen is the endl in the rst and last cout. Unless an endl (or \n) is put in the text, all output will be written on one line; there are several examples of endl in your book in section 2.4. Return Statments The last statement of this program, return 0;, returns control back to the operating system. The value 0 indicates that the program ended normally. The last line of every main function that you write should be return 0; 1.4.6 Syntax

Syntax is the way that a language must be phrased in order for it to be understandable. In English, we say, he runs instead of he run because the syntax of English tells us to place an s on the word run in such constructions. We would probably understand it if somebody said he run, but it would be ungrammatical. C++ has its own syntax as well. The syntax of C++ is more stringent than the syntax of English because if you break any syntax rules in C++, unlike people, the compiler cannot gure out what you mean. One syntax rule in C++ is that every statement must end with a semi-colon. Notice, however, that function headings (e.g., int main ()) do not end in a semicolon. The semi-colons tell the compiler where the end of each statement is just as at the end of every statement in English, we use a period. This punctuation, as well as the // that starts comments, the curly braces and the parentheses around function 4

arguments are all part of C++ syntax. When you compile a program, if it has syntax errors, you will get a listing of those errors and you must x the syntax errors before the compiler can create an executable le which can be loaded and run. The general form of a C++ program is given below: // program name // other comments like what program does and your name # include <appropriate files> using namespace std; int main () { variable declarations; executable statements: } // end main 1.4.7 Testing your Understanding

Throughout this lab manual, there are places where you have the opportunity to check your understanding. This is just such a place. Ask your instructor how you can answer the questions under the section C++ Program Format in Lab 1.

1.5

Checking your Program

Now lets trace through the program by hand to see what will happen when we run it. Knowing how to trace through a program is an important part of programming. It is stepping through the program and doing everything that the computer would do. We do this to make sure that we understand how the program is working and to ensure that it is working correctly. To trace programs, I use a piece of paper with a screen box (i.e., what will appear on the computer monitor), and a box for each variable. Thus, because the initial values of the variables are not known, the initial setup of our hand trace is given in Figure 1. Screen: centimeters: ?? inches: ?? Figure 1: Initial Picture of InToCm program The rst executable statement of the program is cout << "This program converts inches to centimeters" << endl ; After this statement, our boxes appear as in Figure 2. After the next statement, the phrase Enter a number> is added to the screen. If we enter 2.0 as the number of inches, the trace after the third statement is shown in Figure 3. 5

Screen: This program converts inches to centimeters centimeters: ?? inches: ?? Figure 2: Trace of program after rst statement Screen: This program converts inches to centimeters Enter a number> centimeters: ?? inches: 2.0 Figure 3: Trace of program after third statement Calculate the trace after the next three statements and answer the questions under Tracing Programs in lab 1. After executing the return statement, the program returns from main to the operating system. The values as well as the labels for the memory locations that held the values of inches and centimeters are destroyed when the program is complete.

1.6

Synthesis

Most labs will have a synthesis section in which you will write a program based on what you learned. There is, however, no synthesis this week.

Laboratory 2

Errors, Executing Programs, Redirection, Writing C++

2.1

Objectives

In this lab, you will learn how to write a C++ program from start to nish what syntax errors are and how to x them what logic errors are and how to identify them how to redirect input and output what batch and interactive programs are

2.2

Correspondence to Text

The material in this lab best corresponds to sections 2.6-2.8 in the text.

2.3

Compiling and Running C++ programs

Last lab, you examined a C++ Program, intocm.cpp. Lets look at it a little more. Compile your program, and run it. Your instructor will tell you how to do that. Enter 2.0 when the program asks you for a prompt, and hit the return key Print the source code le (our .cpp les are called source les or source code because they contain the source of the compilation C++ statements) because you may want to refer to what a program should look like throughout this lab. Ask your instructor how to print les if you do not know how. Since the actual running of the program matches our hand-trace, this means that the program ran successfully. It is rare that a program will compile and run successfully the rst time that it is tested. Lets look at some problems that can come up.

2.4

Problem Solving

Programming is really all about how to solve a problem. Some of the steps to problem-solving are: 1) analyze the problem to determine the problem inputs, outputs, and constants; 2) develop an algorithm that is, a list of steps to solve the problem (this step includes tracing through the algorithm by hand as we did with the intocm.cpp program); 3) implement the algorithm that is, write a C++ program to correspond to the steps in the algorithm (this step includes compiling the program and removing syntax errors); and 4) test the program and remove any errors. 7

Research has shown, especially when you begin to write larger programs, that the more time that you spend on steps 1 and 2, the less time that you will spend on the total program (all 4 steps). That is, although it doesnt seem that important now, developing a good algorithm and structure chart (discussed in Chapter 3) will save you time in the long run. This lab is just going to concentrate on compiling and running programs, but this should be done only after steps 1 and 2 have been completed.

2.5

Correcting Syntax Errors

You wont always have a program compile successfully on the rst try. Copy the program trip.cpp into your directory or account. Ask your instructor if you have forgotten how to do that. Examine the le. Compile the program. You should get an error listing similar to the following: trip.cpp:9: parse error before { trip.cpp:13: syntax error before < trip.cpp:14: syntax error before < trip.cpp:15: syntax error before > trip.cpp:18: syntax error before > trip.cpp:20: warning: ANSI C++ forbids declaration with no type trip.cpp:20: abstract declarator int used as declaration trip.cpp:20: distance was not declared in this scope trip.cpp:20: speed was not declared in this scope trip.cpp:22: syntax error before < trip.cpp:23: syntax error before < Most of these errors are caused from an unneeded semicolon after the line int main (). Even though there may be nothing to indicate that this line is the source of the error, this one problem causes problems for the rest of the C++ program. Get to know the kinds of errors that appear and what causes them so that it will be easier for you to determine syntax errors in the programs that you are developing. Remove the semicolon at the end of the line that should be: int main (). Compile the program again. This time, you should get an error listing that looks something like the listing below: trip.cpp: In function int main(): trip.cpp:18: warning: initializing non-const bool & with void * will 8

use a temporary /usr/local/egcs/include/g++/iostream.h:216: warning: in passing argument 1 of istream::operator >>(bool &) trip.cpp:18: parse error before < trip.cpp:19: Distance undeclared (first use this function) trip.cpp:19: (Each undeclared identifier is reported only once trip.cpp:19: for each function it appears in.) Lets look at some of these errors. It says that line 18 has an error. Usually that means that there was some problem on or before line 18. Go to the line below: cout << "Enter the distance you will be traveling (in miles)> " Look at the line above that. Do you see any problems with it? It is missing a semi-colon. Add that now. You can recompile the le now or try to x other errors. The next error message might look like: trip.cpp:19: Distance undeclared (first use this function) trip.cpp:19: (Each undeclared identifier is reported only once trip.cpp:19: for each function it appears in.) Do you see the problem with this declaration? Distance should not be capitalized. Remember that C++ is case sensitive. Correct this error. Save the le, and recompile it. This time the program should be error free. If it is not, ask your instructor or try to gure out what your errors are and x them. Now, lets run the program. The le that you run is the executable le and the le with C++ code in it is the source le. You can look at and edit source les, but you cannot run them. You cant really view executable les (you may be able to look at them, but the wont make sense; its just a bunch of weird characters), but you can run them. Run the program trip Enter the numbers 1 for the speed and 20 for the number of miles. What happened? What answer did you expect? What answer did you get? When a program does not run correctly, the program is said to have a logic error. Edit the program (i.e., the source le, trip.cpp) and see if you can see a problem with the last cout. Instead of reporting the time that will be taken, the program outputs the distance. This error was not caught by the compiler because it was valid C++ syntax. It just was an incorrect answer. Change the word speed to time on the last cout line. It should now read: cout << "miles will take you " << time << " hours.\n"; Save the program, compile it, and run it again using the same test data (1mph and 20 miles). This time the output should be: To travel at 1mph for 20 miles will take you 20 hours. Great! It nally works. 9

Lets test it once more using dierent numbers: 10mph and 40 miles (we know that it should take 4 hours to do that). Run the program, and see what happens. This is the output you get: To travel at 10mph for 40 miles will take you 400 hours. WOW! Thats a long trip. The program seems to work only for selected data. It is important to check your program with dierent data to make sure that it works with a wide variety of data. Lets edit the program and see whats wrong. Look at the assignment statement. If you remember your physics, you know that distance = speed time so if we are looking for the time, then time = distance/speed We used the wrong formula! It just happened that our rst test worked because the result of multiplication and division were the same. Change the multiplication operator to a division operator (/) and save the le, and compile it. Test the le one more time with both test data sets (1 and 20 and 10 and 40). This time you should get the correct answer.

2.6

Redirection

Sometimes we want to enter data via the keyboard, but sometimes, it would be nice if we could do something else. Run the intocm program again. Use 2.0 as the test value. Running a program in this way is referred to as interactive because as the program is running, the user (i.e., the person running the program you) is entering data and getting feedback. Not all programs are run this way. For example, a computer program probably calculates your paycheck. That program almost certainly does not have a user at the keyboard typing in information; instead, the program reads all of the information from a le (e.g., an employee le that contains a list of all employees and what their pay rate is), and then at the very end prints out a list of paychecks. Unless something goes wrong with the payroll program, it probably does not interact with the person who initiated the program. This kind of a program is referred to as a batch program. It runs with no interaction from users. 2.6.1 Input Redirection

The advantage of a batch program is that the information does not need to be entered every time the program is run the input can be kept in a le (imagine how many errors there would be and how much time would be taken if somebody had to type in all employees names and pay rates every time checks were issued). Another advantage is that we can test a program over and over without having to enter the data each time. If the data is in a le, it just has to be entered once. 10

So far, the amount of data (one number) that we have been entering is small, but it can get long and tedious to test a program that has a long list of inputs. We can put the input for a program in a le (e.g., a le named inputfile), and the program will read any input from the le instead of from the keyboard. On systems with a command line interface (that is, you enter commands at a prompt), you can do this by typing: program when you normally would have typed program to execute program. This is called redirection (more specically, this is redirecting the input). Ask your instructor how to use redirection on your system. Lets see how batch les dier from interactive les by redirecting input from the keyboard to a le when running intocm. To do that, create a le inchin that has just the number 2.0 in the le. Save the le. Run the intocm program using inchin as the input. If using a command line interface, type intocm <inchin The program should run from start to nish without asking the user (you) for input because the input is provided by the inchin program. When programs are run using les as input instead of keyboard responses, the prompts associated with asking the user for a value are sometimes deleted because they are not needed, and may even confuse somebody watching the program run (in this case the prompt is Enter a number>). 2.6.2 Redirecting Output <inputfile

Just as input can be redirected from the keyboard to a le, output can be redirected from the screen to a le. When using a command line interface, the input redirection operation is <; the output redirection operator is >. Thus, to redirect the output to a le out instead of to the screen (using a command line interface), type program >out

Redirecting the output is normally not used by itself if the input is going to be read from the keyboard. To see why, run intocm redirecting the output to the le cm. If using a command line interface, type:

11

intocm

>cm

What happens? The program just sits there. Actually, its waiting for you to enter a number of inches. Type 2.0 and hit the return key. You should get a prompt again. Because the prompts to tell you what to enter went to the output le, it was dicult to know what to do. Look at the output le cm. Do you see the prompts there? Normally, programs that have their output redirected run completely in batch mode where both the input and output and redirected. When output is redirected to a le, it can be examined later for accuracy or it can be used as input for another program. Once the information is written to a le, it is more permanent than information displayed on the screen and can be used for a number of purposes. Run intocm again, redirecting both its input and its output: Type (if you are not using a command line interface, ask your instructor): intocm <inchin >cm

(If you get a message that cm already exists, remove the le cm, and then enter the command above again.) Look at cm to make sure you have the right answer. Lets see if you understand redirection. Answer the questions under the Redirection section of lab 2 questions. 2.6.3 Redirecting Errors

In addition to redirecting input and output, you can also redirect any syntax error messages that the compiler generates. It is often useful to have a le that contains all of your syntax errors. Ask your instructor how to do this on your system.

2.7

Synthesis

Now lets write a program. We want to write a program to convert feet to yards. First, of course, we need an algorithm. Fortunately, the algorithm is in the le that you have copied. For the actual writing of the program, you may want to refer to the intocm.cpp program as an example. Edit the program, and do the following: Copy the le feettoyards.cpp. First, add your name in the second line as a comment so that people know who wrote the program.

12

Second, write a compiler directive to include the le iostream using earlier programs as an example if you dont remember how to do this. Third, add the using namespace std; line to indicate that objects are in the standard namespace. Next, write the program format in the program. That is, right after the line that says: // begin the program by writing the function main and the program format, write: int main () { The variable declarations will go immediately following the open curly brace. Put a close curly brace at the very end of the program after all other lines. This corresponds to the general format of a C++ program given earlier. Next, add the variable declarations for feet and yards under the lines // declare the variables that we will use: // feet will hold the input (of type oat) // yards will hold the output (of type oat) Make sure that you have a semi-colon at the end of the line that declares the variables. Name the variables feet and yards. Next, put a blank line separating the variable declarations and the executable statements and start the executable statements. First, underneath the comment // write a statement about what this program does, write a statement to the user indicating what this program does: cout << "This program converts feet to yards." << endl; Be sure to write the output operator as << instead of >>. Next, underneath the line // write message to enter feet and read value into variable feet write a message to the user to enter the number of feet (use: Enter Feet> Next, write a cin statement to read in the number of feet into the variable feet. This one comment covers two C++ statements. In general, you will not have one comment for every line of C++ code. That would be distracting and hard to read exactly the opposite of why you have comments in the rst place! Look at the intocm.cpp le and see if you can tell how to read a value into the variable feet based on how you read a value into the variable inches. After you have written statements to read in the data (which you did in the previous step), write the necessary statement to do the calculation under the comment that says: // calculate conversion using formula yards = feet / 3 Next, you will need to write the C++ code to output the answer. It is good programming practice to echo the input as I have in the intocm.cpp program; that is, when you write 13

the answer (which in this case will be the number of yards) also write the input (which in this case is the number of feet). I have written this cout command for you in a comment. All you need to do is remove the // at the beginning of the line. Finally, you should put return 0; as the last line in your main function. You have just nished writing your rst C++ program. Now, compile it and run it. Test it using the following test data: 0.0 feet, 10.0 feet, and -3.0 feet. Make sure you understand how to write, compile, and run a C++ program because you will be doing this process the entire semester. Now, create a le infeet that has just the number 10.0 in it. Run the program feetyards, and redirect the input to be from infeet, and redirect the output to be to a le called feetout.

14

Laboratory 3

Expressions, Libraries, and Functions in C++ Libraries

3.1

Objectives

In this lab, you will learn more about expressions and input how to use functions what C++ libraries are what functions are available in some C++ libraries more about writing C++ programs.

3.2

Prerequisites

You should know how to write, compile, and run a C++ program in order to do this lab.

3.3

Correspondence to Text

The material in this lab best corresponds to 2.4-2.6, 3.1-3.2, and parts of 3.5 and 3.7 in the text.

3.4

Introduction

You have already seen one function in all of the programs that you have written. That function is main. But most programs have many other functions. And there are often functions and other information contained in header les also. You have also used header les in the past: the #include <iostream> statement includes the header le called iostream, which includes C++ code that allows you to use cout and cin.

3.5

Input and Output

Before we look at functions, lets look briey at more input and output. In the programs that we have examined so far, we have only entered one value at a time. But cin, like cout can have a number of values on which it operates in one statement. For example, if we were trying to calculate the area of a rectangle, we could do it by entering the length and the height one right after another. Copy the program recarea.cpp. Compile the program.

15

Run the program. When the program asks you to enter the height and length, enter 3 and 4 separated by a space. Now view the program. Notice how the cin statement reads one value after another. Also, notice that the data values are int and not float. We could have declared them as oat, and used the same cin statement. I just wanted to give you an idea of how integer values work in C++ because the other variables that weve been using have mostly been of type float. Answer the questions in the cin section of Lab 3 Questions. Remember that the C++ input stream operator is >> and not <<; the input redirection operator is <; the output redirection operator is >.

3.6

Evaluating Expressions

Before talking about functions, it will help you if you get a little more practice evaluating expressions and learn a little more about the built-in data types char, float, and int. 3.6.1 Data Types

Each variable is assigned a type. You will create your own types later on, but for now, we can use the three types named above. When a variable is declared to be of some type, the values that that variable can have and the operations that can be performed on it are dened. For example, if a variable is of type oat, it can have values like 2.0, -3.46, and -234.46e-03, and you can do things like reading oats in and writing them out and performing arithmetic operations on them (this is all discussed in your book in more detail). Variables of type char on the other hand have values like a and ?, but it usually would not make sense to try to add and subtract variables of type char. Variables of type int have values like 355, -2543, and 0 and you can perform arithmetic operations on them just like you can with floats. Because int numbers have only integer parts, division gets a little complicated; the result of dividing one integer number by another is still an integer. So what do we do with an expression such as 8/5? The result of this expression is the whole number answer; the remainder is dropped. In this case, 8/5 = 1. Sometimes, though, the remainder is important (in fact, the remainder is often more important than the whole number part), so C++ gives us a way to nd the remainder. The operator is called the modulus operator and is written %. Thus, 8%5 = 3 because the remainder of 8 divided by 5 is 3 (1 with a remainder of 3). The main dierence between floats and ints (for right now) is the way division is done and that floats have a decimal portion and ints cannot. What happens when you add two numbers, one of which is type int and the other of which is type float? For example, what is the result of 243 + 34.456? The result is 277.456 which is a number of type float. When ints and floats are used together, the result of the operation is of type float.

16

3.6.2

Order of Operations

What happens when you have an expression that has several dierent operators? Which one do you do rst? For example in the expression (14 + 6) - 8 / 2, what is the answer? The order of operations for C++ is the same as the order of operations that you learned in your math class. From left to right in the expression, this is the order: 1. First, expressions inside of parentheses are evaluated. 2. Next, the multiplication operators (*, /, %) are performed. 3. Finally, the addition operators (+, -) are performed. 3.6.3 Practice

Look at the following chart and make sure that you understand why each result is correct. Expression 10 % 3 10 / 3 10.0 / 3.0 10.0 / 3 3/4 3%4 (3 + 4) / 6 - 2.0 * 8 Resulting Data Type int int oat oat int int oat Result 1 3 3.33333 3.33333 0 3 -15.0 Notes

(int 3 converted to oat)

order: 3 + 4 = 7 7/6 = 1 2.0 * 8 = 16.0 1-16.0 = -15.0 15.0 will be displayed as 15

Answer the questions in the Evaluating Expressions section of Lab 3 Questions 3.6.4 Expressions in Programs

Lets write program to calculate the volume of a sphere. You may remember that the volume of 3 a sphere is: volume = 4 3 r Copy the program sphere.cpp. This program is already written except the line that performs the volume calculation. Write in that line. Use 3.14 as the value of pi. Remember that dividing by two integers results in an integer answer. For now, just write r3 as r * r * r. Do not modify any of the cin or cout statements or your output will not match mine when you test it later on. Test the program using the value 2.0. Your answer should be 33.4933.

17

3.7

Libraries

We talked briey in the rst lab about compiler directives and the #include <iostream> line in all of our programs. I want to talk about that more now, and what that means. Programmers are inherently lazy people, and do not like to do work twice. For that reason, programmers reuse code that already exists whenever possible. This reuse has many advantages. If a programmer thinks that a portion of C++ code is likely to be reused by the programmer or by others, that programmer might want to save that portion some place where others can access it. These commonly reused portions of C++ code are stored in libraries. A library is simply a le or collection of les that contains C++ code that might be useful. 3.7.1 Including Libraries

You may create your own personal library, but libraries are also included with a compiler. Including C++ code from a library into your own program is easy youve been doing it since the beginning of the semester. Just use a compiler directive called include to name the le (remember that all compiler directives start with the # symbol). Copy intocm.cpp (copy it again if you still have it from earlier labs). Edit the le. Delete the line that begins #include. Compile the le. What happens? You should get these errors (or a similar listing): intocm.cpp: In intocm.cpp:12: intocm.cpp:12: intocm.cpp:12: intocm.cpp:12: intocm.cpp:14: function int main(): cout undeclared (first use this function) (Each undeclared identifier is reported only once for each function it appears in.) endl undeclared (first use this function) cin undeclared (first use this function)

These errors occur because the compiler cannot nd the C++ code for cout and cin since this code is contained in the le iostream, and iostream is no longer a part of the C++ program intocm.cpp. Put the line #include <iostream> back in the le intocm.cpp, and recompile it. What happens now? With the header le back in, the code for cout and cin is found again, and the program should have no syntax errors. 3.7.2 What is a library?

Library les are les that contain compiled C++ code. They usually have header les associated with them (les which are read into your program with #include). Some standard C++ header les are cmath, iostream, cstdio, and string. Each of these les contains C++ code that allows you to use code in your own personal programs that was written by somebody else. For example, 18

if you wanted to calculate the value of some number cubed (i.e., x3 ), you could multiply the number by itself three times (x * x * x), but it would be nice if you could have had a function to calculate x raised to the third power instead of writing x * x * x. There is a function called pow in cmath that does just that. If we include cmath, then we can use the pow function instead of writing x * x * x. Many other useful functions exist in these header les as well. Your book lists some of the libraries available with most C++ compilers, including some of the functions that are available in cmath; and many functions that are available in dierent libraries along with other relevant information for that function. Examine Appendix C in your text which discusses these functions and answer the questions in the section C++ Libraries of lab 3 questions. 3.7.3 Whats in a header le?

A header le contains no special code just standard C++ code. If you look at a header le, it may look strange compared to the C++ code that youre used to seeing, but that is because we havent learned some of the constructs used here; its not because there is anything mysterious about this code. The C++ libraries on one system are in /usr/local/egcs/include/g++ and /usr/include. Ask your instructor where your C++ header les reside. Look at the le cmath in the directory that contains the C++ libraries on your system. The very rst line is a C++ comment. You can tell because it begins with the two symbols // There is some other stu, and then the line: #include <math.h> (Note that this include le uses the .h extension, while others did not. This is because it is part of the Standard C library, not part of the Standard C++ library). This shows that header les can include header les also. The next couple of lines contains something close to variable declarations; float and long double are kinds of oating point numbers. You can look at other header les and you may see some C++ code that you recognize. The point is that header les contain C++ code that you could have written (or will be able to write eventually!). 3.7.4 Creating a header le

So why dont we write our own header le? Lets say that the variables inches and centimeters are much in demand, and will be required by many programs. We can put those variable declarations in a le by themselves. Since were going to put inches and centimeters in a le by themselves, take them out of intocm.cpp. Edit intocm.cpp, and delete the line that says float inches, centimeters; (make sure that you leave the opening curly brace which begins that function main). Now, create a le called intocm.h Unlike system include les like iostream, your own include les should end in .h. Put the line: float inches, centimeters; in intocm.h. Save the le. 19

Compile intocm.cpp. You should get the following syntax errors (or something close): intocm.cpp: In intocm.cpp:15: intocm.cpp:15: intocm.cpp:15: intocm.cpp:16: function) function int main(): inches undeclared (first use this function) (Each undeclared identifier is reported only once for each function it appears in.) centimeters undeclared (first use this

Why did that happen? It happened because we deleted the declaration of inches and centimeters, and we didnt include the declaration any place else. Edit intocm.cpp, and add the line: #include "intocm.h" immediately after the line #include <iostream>. Note that you use quotation marks, and not angle brackets. Whenever you create your own header le, use quotation marks. This tells the compiler to look in your directory, not the standard include directories. Compile the le again, and run it. This time it should run just like it would have without the changes. 3.7.5 Using a Math Library

Remember writing sphere.cpp? It would be nice if we could just use a value for that was already dened somewhere (like maybe, in a library), and if we could use the pow function instead of writing r * r * r. We can do that by including cmath in our le. Copy sphere.cpp to sphere2.cpp. Edit sphere2.cpp, and add the compiler directive #include <cmath> immediately after the compiler directive #include <iostream> (on separate lines). Change your value of (3.14) to be M PI. M PI is a constant in the le cmath. It is accurate to 20 decimal places 18 better than we just did! Change where you have written r * r * r to be pow(r,3) Compile the program. Run the program using 2.0 as a test value again. This time your answer should be 33.5103 (the extra accuracy of is the reason for the dierence).

3.8

Functions

In addition to reusing existing code, programmers also like to break problems down into subproblems. Subproblems are often implemented as functions in C++. Functions in C++ can perform calculations and return a value. For example, pow returns the value of a number raised to a power. When you used pow, you were calling or invoking a function. 20

3.8.1

Information for invoking functions

Functions in C++ can have arguments. Remember that arguments to functions tell the function what objects to use in performing its task. For example, in order to raise a number to a power, the function must know what the number is and what the power is. This information is passed to the function in terms of an argument. When you typed pow(r, 3), you were telling the function pow to calculate the value of r raised to the third power, and to return that calculated value. The value returned may be used as part of an expression (as it was in this case), or in any way that a value of the return type can be used, e.g., as another argument to another function, (e.g., log10(pow(100,2)) would return the base-10 logarithm of 1002 , which is 4). Look in your book in Appendix C. Look at the function pow. The information there says that pow has 2 arguments that are of type double (that is, a real number type like float). It also says that pow returns a number of type double. Lets look at another example. The function isdigit has one argument, a character, and it returns an integer (0 if the character argument is not between 0 and 9, and some non-zero number if the character argument is between 0 and 9 this part is not included in the explanation in your book; I just knew that). So, isdigit(3) would return a non-zero number and isdigit(a) would return 0. You can see that the three pieces of information that are critical when calling a function are: 1) the name of the function; 2) the number and type of the arguments (if any); and 3) the return value of the function (if any). Note that not all functions have return values, and not all functions have arguments. 3.8.2 Invoking Functions

When you write your own functions, you will need to dene the function and you will need to declare the function (just like you declare variables). Well discuss how to dene and declare a function next week; for the functions that exist in libraries, the functions have already been dened and declared. To call a function, use the C++ code: name-of-function(arg1, arg2, ... argn) where name-of-function is the name of the function and the arg1, etc. in the parentheses is the list of parameters. For example, you called the function pow by typing pow(r, 3) (r and 3 were the arguments to the function). This example shows that the order of the arguments matters. pow(3, 4) is 81 (34 ); pow(4, 3) is 64 (43 ). Another example of calling functions is by including them in a cout statement. If you wanted to print whether the letter a was a digit, you could include the statement cout << " a is a digit is " << isdigit(a) (of course the person reading this output would have to know that a non-zero answer means that a is a digit and that a zero answer means that a is not a digit). All of the C++ code that you have written so far has been in the function main. Thus, when you typed pow(r,3), you were invoking the function pow from the function main. main in this case is referred to as the calling function or the caller. pow is the called function. main is like other functions; it can have arguments, and it can return a value (why arguments and return values might be useful to main will not be covered in this course).

21

3.8.3

Return Values for Functions

Functions can be said to be of certain types. A functions type depends on its return value. If a function returns a value of type int, then the function is of type int (int is in fact the default type). If a function has no return value, it is void. Back to the functions that are in Appendix C in your book: function pow is type double because the return value of pow is double. double is a type similar to float. Edit your le intocm.cpp, and include the statement cout << " a is a digit is " << isdigit(a). This statement doesnt make much sense in this program, but it does show how the function isdigit should be invoked. Pay attention to the use of a single quote () versus a double quote ("). Be sure to include the compiler directive #include <cctype> on the line after #include <iostream> so that the compiler can nd the function isdigit. Compile and test your program. Answer the questions under Functions in lab 3 questions.

3.9

Compiling Multiple Files together

There wasnt much code in the header le "intocm.h", but most C++ programs contain a great deal of information external to the .cpp le that you yourself write. Therefore, it is sometimes helpful to compile the other les separately. This also allows you to create les that can be used over and over by dierent .cpp les. Lets look at how this works. Lets pretend that the function pow does not exist. Instead, we want to write our own; well call it power. Because this function will be used by many dierent programs, we want to put it in a header le so that it can be included whenever necessary. One way to do this is to create header and implementations les power.h and power.cpp that have a declaration and a denition for the function. That way, we can just include power.h in every program that uses it. Lets do that. Copy the les power.h and power.cpp. Examine both les to see what they look like. Do not worry if you do not understand exactly how the power function is calculated. Copy sphere2.cpp to sphere3.cpp, and modify sphere3.cpp to include the le power.h (after #include <cmath>, add #include "power.h"). Modify pow to be power. Compile sphere3.cpp. What happens? You should get an error that says something like: Undened symbol power Fdd ld: fatal symbol referencing errors. no output written to a.out Why? The reason is that the function power is no longer dened anywhere. Its dened in power.cpp, but we havent included that in any way. What we need to do is include power.h and have the code for power.cpp somehow added to the executable le. We can actually do that. Ask your instructor how to change your compile sphere3.cpp with power.cpp. The compiler will nd compile both together into one executable le. 22

Because it takes a while to compile, it might be quicker, especially if were going to use power.cpp over and over if we keep it compiled all of the time. Try compiling power.cpp. What happened? You dont have a main function. Do you remember when I told you that every program has to have a main function? But it still seems silly to compile it from scratch each time its used. C++ allows you to compile separate object modules: intermediate les which can linked, or combined, into one executable module. Compile power.cpp into an object le, not an executable le. Ask your instructor how to do this. You can now use this object code when compiling sphere3.cpp or any other source code le that uses power.cpp. Compile sphere3.cpp with the object le created by power.cpp (it may be called power.o). Ask your instructor how to do this.

3.10

Synthesis

Write a program synthesis.cpp that reads in a base and an exponent, and that calculates and outputs baseexponent . For example, if the user entered 10 for the base and 3 for the exponent, the program would output 1000 (which is 103 ). Use the function pow in cmath. Write and compile synthesis.cpp. Test the program. Test the program by running it at least twice, rst with the numbers 3.04 and next with the numbers 4.03 .

23

Laboratory 4
4.1 Objectives

Writing Functions

In this lab, you will learn how to declare functions what a function declaration is how to write a function what arguments and return values do and how to use them

4.2

Prerequisites

In order to do this lab, you should know how to write, compile, and run a C++ program, and you should understand what functions are and how to invoke them. You should also be familiar with an editor.

4.3

Correspondence to Text

The material in this lab best corresponds to 3.3-3.5 in the text.

4.4

Introduction

In the last lab, you learned how to invoke functions. In this lab, you will learn how to write your own functions, and call them from other functions.

4.5
4.5.1

Functions without Arguments or Return Values


Writing Functions

Breaking problems down into subparts, and putting the subparts into functions has many advantages. The subparts can be reused several times in the same program or in other programs; they can be modied without changing any other part of the program; and the details of how the subparts are solved are hidden from the higher level functions. One function that you might want to have for any program that you write is a function that prints instructions to the user telling the user how to run the program. Copy restaurant.cpp. Look at the program to see the function instruct user. The function is after the main function starting on line 33.

24

4.5.2

Examining the Function

The rst line of the function is: void instruct user () This line is called the function header, and there is no semicolon at the end of the line. The function body is formed by curly braces; it looks just like the body of main (i.e., you can put the same things in a function body as you can put in main because main is a function). The entire function function header and function body is referred to as a function denition. 4.5.3 Prototypes

Lets go back and look before main. After the include statements, you should see the following line: void instruct user (); // function prototype This is called a function prototype; it declares the function just as variables are declared. It contains the return type of the function (void for this function; recall that void indicates that no value will be returned from this function) and the type of any of the arguments (none for instruct user). For every function denition, there should also be a function prototype. For many functions, however, the prototype (or function declaration) and denition are written outside the main program le. This was the case for pow last week, for example. Both the function prototype and denition were in other les. Look at the rst line past the variable declarations in the main function. This line, instruct user (); calls the function instruct user. This call is similar to calls to isdigit and pow from the last lab. The main dierence is that there are no arguments in this call. Note that even though there are no arguments, there are still ()s. Answer the questions under section Functions without Arguments of Lab 4 questions. In addition to the main and instruct user functions, there is another function, calctotal in restaurant.cpp. This function calculates the cost of a meal based on the cost of the food and the beverage (15% tip and Virginia food tax of 9% are also included). Look at this function, and use it as reference later when writing functions with arguments and functions that have a return value. Compile and run restaurant.cpp. Use $9.00 for the price of food, and $2.00 for the price of drinks (be sure to leave o the dollar sign when entering the numbers). Notice that the answer is not displayed in terms of dollars and cents. It would be nice if we could round the answer o to the nearest cent as restaurants actually do. Chapter 5 discusses how to do that. For now, well have to settle for too precise an answer. 4.5.4 Writing Functions

Lets write a similar function in another program. Copy the le balance.cpp. 25

Add an instruct user function that is similar to the one in the le that you just examined. Edit the le, and add a function prototype, a call to the function, and a function denition. In the function denition, write instructions to the user on how to use this program. Save, compile, and test the program.

4.6

Functions with input arguments

If all we could do is write instruct user functions, functions would not be very useful. But you know that functions can have inputs arguments as well that allow much more exibility. You used pow with several dierent input arguments last lab. Lets write our own function that has input arguments. In this case, were going to write a function that outputs the message hi xyz where x, y, and z are going to be input to the function. So, the same function can print Hi Mom, Hi Dad, and Hi Dog(this may not sound terribly exciting, but in fact, this capability oers a great deal of power, especially when used with return arguments). Copy mom.cpp. Compile and run the program. Enter Dad at the prompt. Type the return key. Run the program again, typing Mom this time at the prompt. You can see that the program produces dierent output depending on the input. Now, look at the program. Examine the function prototype and the function denition. Write a second function, printbye that takes the same arguments and prints bye, xyz (where xyz are the three characters that you entered). First, write the function call immediately below the call to printhi in the main function. You do not need an additional cin statement because you have already read in the data before the printhi call. After writing the call to printbye, write a function prototype immediately following the printhi prototype. Make sure that you understand what is calling the function and what is declaring the function. Finally, write the printbye denition, which should be identical to the printhi denition except it should cout Bye instead of Hi. Remember that function headers do not have a ; at the end of the line, unlike function prototypes, variable declarations, and executable statements. Compile and test the program. Answer the questions under the section Functions with Arguments of lab 4.

4.7

Functions with return values

We still havent done functions that are terribly interesting for example, we still couldnt write pow or any other function that performs some kind of calculation because we havent talked about returning values from a function. It is possible to return a value calculated in a function by using 26

the return statement. For example, our beloved intocm.cpp program could be rewritten with the calculation being performed in a function, and the result of that calculation could be stored in centimeters and returned to a calling program. Lets see how thats done. Copy the le intocm.withfunc.cpp into your directory. Compile it and test it. Look at the function prototype (float intocm(float);). This states that the function has one argument which is a type float (this is given in the parenthesis), and that it will return a value that is of type float (thats what the float before the intocm means). Look at the function header (float intocm(float inches)). It states that there is one argument of type float, and the name of the argument inside the function will be inches (the calling function could call this argument a completely dierent name). The body of the function consists only of a return statement that returns a value of the type float. Functions can be much more complicated than this. Look at another example of a function. Copy distance.cpp. This program calculates the distance between 2 points. Compile it. Test the program with several dierent points. Use (3,3) and (4,4) as one test and (4,10) and (14, 10) as a second test. When you enter the points, just enter numbers; do not enter parentheses or commas. Look at the two functions denitions, sqr and diff. They show that you can calculate the value, assign it to a local variable then return the value of the variable (as in sqr), or, if the calculation is a relatively simplistic one, the calculation can be performed in the return statement itself (as in diff). Write whichever seems the most natural to you, and the one that you think is the easiest to read. You will notice that the program also has a statement about the slope of the line, but the slope is currently not being calculated correctly. Lets modify the program to nd the slope of the line. The equation for the slope of a line given two points (x1 , y1 ) and (x2 , y2 ) is: slope = (y2 y1 )/(x2 x1 ) Modify distance.cpp so that it calculates the slope of the line as well as the distance. Modify only the one line that reads: // slope = put slope expression here Remove the comment, and put the correct calculation. You should use two function calls in your calculation; (there are ve function calls in the distance calculation; from left to right, they are: sqrt sqr, diff, sqr, diff). Recompile and test the program. Answer the questions in the final set section of Lab 4 questions. Answer the questions in the Mixed Bag section of Lab 4 questions. 27

4.8

Synthesis

Write a program tri.cpp that calculates the time that it will take to complete a triathlon. A triathlon has three parts: a swim leg, a bicycle leg, and a running leg. There will be 4 constants in the program: the swim length, the bike leg length, the run leg length; and the athletes transition time (transition time is the length of time that it takes to go from one leg to the other; this typically involves putting on or taking o some clothes or equipment, such as a bicycle helmet). All lengths are in miles; the transition time is in minutes; and the pace/speed will be in miles per minute. In order to calculate the total time, you will ask the user/athlete for the athletes pace (all in miles per minute) for the swim, bike, and run (so you will have three inputs, all of which will be of type oat). The total time is given by the formula: totaltimem = timem (swim) + transition timem + timem (bike) + transition timem + timem (run) That is, the total time (in minutes) equals the total time of the swim (which comes rst, so people are fresh and dont drown) plus the transition time (from swim to bike) plus the total bike time plus the transition time (from swim to run) plus the total run time. All times are in minutes (thus the subscript m ). Because you are calculating the time to complete a particular part three times, this part of the problem is an excellent candidate for a function. Therefore, you should write a function, CalcLegTime, which takes the speed and distance of a particular leg as an argument and returns the time for that particular leg. For example, if a person bikes at 0.3 miles per minute (thats 18mph) and the bike course is 14 miles, it will take about 47 minutes to complete that part of the course. In this case, CalcLegTime would have 0.3 and 14 as arguments and would return 46.66... (14/0.3 or distance/speed). You should call this function three times: once to get the swim time, once to get the bike time, and once to get the run time. To get you started, I have written a program shell that you may copy into your directory. It is called tri.cpp. Copy tri.cpp. Write the function CalcLegTime and write the rest of the function main. The input and output statements are already written for you. Compile and test your program.

28

Laboratory 5
5.1 Objectives

Selection Statements

In this lab, you will learn how to write and evaluate logical expressions how to write if statements how to write if-else statements how to write nested if statements how to hand trace if statements

5.2

Prerequisites

In order to do this lab, you should know how to write, compile, and run a C++ program, and you should be able to call and write functions.

5.3

Correspondence to Text

The material in this lab best corresponds to sections 4.1-4.7.

5.4

Introduction

So far, the programs that you have written perform every statement. Problem-solving, however, often involves choices. In order to make decisions, programming languages provide some statement that tells the system to perform some statement(s) if some condition exists and possibly some other statement(s) if that condition does not exist. For example, if we are making a cake, we check it after some amount of time. If it is done, we take it out of the oven. Otherwise, we leave it in the oven. If we are deciding what to wear in the morning, we check the weather. If it is warm, we wear short-sleeves; otherwise, we wear long-sleeves. If we were writing a program to choose the larger of two numbers, we would choose the rst number if it was greater than the second; otherwise, we would choose the second. In the constructs that we have learned so far, there is no way to express that.

5.5

Logical Expressions

In order to write the statements that allow us to make these decisions, we need to have some way of evaluating the alternatives. Expressions that allow us to do this are called logical or boolean expressions. Logical expressions evaluate to be either true or false. For example, to say that the value in x is greater than the value in y, we could write: x > y

29

This expression would evaluate to be true if x is greater than y and false otherwise. 3 < 4 is true (but 4 < 4 is false); 3 > 4 is false (4 > 3 is true); and 3 == 4 is false (4 == 4 is true); 3 <= 4 is true (as is 4 <= 4), and 3 >= 4 is false (but 4 >= 4 is true). The relational operators (i.e., the operators that compare values) are: <, >, <=, >= and == for equals (do not confuse =, the assignment operator, with ==, the equal relational operator). Copy evaluate.cpp. Compile it. Run it to see what dierent expressions evaluate to be. You may use only numbers and the three relational operators <, >, and ==. Do not use <= or >= For example, try 234 == 234. Would you expect this to be true or false? Try 234 == 5735, 253 < 1123, 2443 < 2245, 245 > 123 and any others to get the idea of how the expressions are evaluated. 5.5.1 Compound Boolean Expressions

Expressions can get more complicated than just using the relational operators. Sometimes it is necessary to determine if a value is within some range. For example, to determine if a number x is a single digit, it must be greater than or equal to 0 (x >= 0) and it must be less than or equal to 9 (x <= 9). To write such an expression in C++, you would write: (x >= 0) && (x <= 9) (the parentheses are not needed, but add clarity). The && operator is the and operator that links two logical expressions. Other logical operators are which is the or operator and ! which is the not operator. Examine the following table and make sure you understand why the expressions evaluate to the given values. In the table, x has the value 25 and y has the value 14. a. Expression (24 < y ) && (13 < x) Result false Notes 24 < y is false 13 < x is true false && true is false x > 23 is true x == y is false true false is true x > y is true !true is false x == 14 is false y < x is true !false is true true && true is true

b.

(x > 23)

(x == y )

true

c. d.

!(x > y ) !(x == 14) && (y < x)

false true

Do the lab 5 questions in section Simple and Compound Logical Expressions.

5.6

If Statements

Logical expressions are great, but how do we use them in C++? C++ has an if statement that allows the program to choose which statement to perform based on how a logical expression is evaluated. For example,

30

if (grade >= 60) cout << "passing"; else cout << "failing"; would print passing if grade >= 60 evaluated to be true and failing if grade >= 60 evaluated to be false. I will refer to the part inside of the if statement (cout << "passing";) as the if clause and the part inside the else (cout << "failing";) as the else clause. In an if/else (sometimes just called if) statement, exactly one of the two clauses is performed: either the if clause is performed or the else clause, but not both. Note that the logical expression is inside parentheses. These parentheses are required C++ syntax. Heres another example: if ((a >= 0) &&; (a <= 9)) digit = 1; else digit = 2; This statement assigns digit to be 1 if a is between 0 and 9, and assigns digit to be 2 otherwise. Here, the if clause is digit = 1;; the else clause is digit = 2;. Copy program firstif.cpp. Edit the le, and write an if statement so that greatest will be x if x > y and y otherwise. Place the if statement right after reading in the variables x and y. You should write one if statement that has a logical expression comparing x and y. The logical expression should be in parentheses, like the if statements above. The if clause should be an assignment statement to greatest; be sure to put a semi-colon at the end of the statement. Next, write the word else, and on the next line the else clause, which assigns greatest to the other value if the logical expression in the if is not true. Compile the program, and test it three times with the rst number (x) being 4 all three times. The second number (y) should be 10 the rst time, -3 the second time, and 4 the third time. What do you expect to happen when the second number is 4? Which variable (x or y) is assigned to greatest?

5.7

If statements with no else

Sometimes, we want to do a particular statement if some condition is true, but we do not want to do anything special if the statement is not true. There is another form of the if statement for this purpose. For example, we may want to print a warning message if a student is failing, but do nothing if the students grades are passing. if (grade < 60) cout << "failing"; In this case, there is an if clause but no else clause. There are two forms of the if statement: 31

1) if (logical expression) statement; else statement; and 2) if (logical expression) statement; We can modify the greatest program to assign x to be the greatest before any if statement, and then assign y to greatest only if y > x. Copy secondif.cpp. Write the if clause associated with the if statement so that y is assigned to greatest when y > x. Do not change any of the rest of the program.

5.8

Compound Statements

We can write if statements now, but what if we wanted to do more than one statement as part of the if clause or as part of the else clause. For example, we might have a program that calculated the area of dierent shapes. If you calculated the area of a rectangle, you would need the base and the height as you would if you calculated the area of a triangle, but if you calculate the area of a circle, you would need the radius, not the base and height. That program might have the following code segment cout << "This program calculates areas of shapes." << endl; cout << "Enter a T for triangle, an R for rectangle, and a C for circle "; cin >> choice; if ((choice == T) || (choice == R)) // read in the base and height for calculating area else // choice == C // read in the radius for calculating area The statements inside these clauses will take more than one statement. Inside the if clause, you might want: cout << "Enter the base "; cin >> base; cout << "Enter the height "; cin >> height; Inside the else clause, you might want: 32

cout << "Enter the radius "; cin >> radius; I have written a program area.cpp that does exactly this. Copy area.cpp. Look at the if statement in the program. Compile the program. What happens? Why? You will get a syntax error because the else has no if to go with it. But why is that? The reason is that only one statement is allowed inside an if clause and only one statement as part of an else clause. In order to have more than one statement as part of an if or else, we must make a compound statement. To make a compound statement, put an opening curly brace before the rst statement, and a closing curly brace after the last statement. This should look familiar. This is what you do for all functions. The format of a function is the function header and then the function body which consists of a single compound statement (which may contain multiple simple statements), that is: returntype functionname (function args) compound statement Edit the le area.cpp to make the statements inside the if clause one compound statement and the statements inside the else clause into one compound statement; that is, put curly braces around the statements in the if clause and curly braces around the statements in the else clause. Compile the program. Test the program using the choice T with the numbers 3 and 4; with the choice R with the numbers 3 and 4; and with the choice C and the number 2. 5.8.1 Style Note

It is important to adopt a consistent style of indenting, and use it in all of your programs to make the programs clear. Always indent a couple of spaces on the line after an if and an else. If the statement inside an if or else clause is a compound statement, all of the statements within the compound statement should line up. There may be compound statements in only one of the if or else clauses, or in both or in neither. Do questions under the section Simple and Compound If Statements in lab 5.

33

5.9

Nested If statements

All of what weve done so far is great, but what if we want to check more than just 2 conditions. For example, we have calculated the greater of 2 numbers, but what if we wanted to output which is the greater if they are not equal and that they are equal if they are. We could do it this way: if (x > y) cout << x << " is greater than " << y << endl; if (x < y) cout << x << " is less than " << y << endl; if (x == y) cout << x << " equals " << y << endl; But this is inecient because if x is greater than y, then we shouldnt have to make the last two comparisons. More importantly, however, the code does not reect that these comparisons are not independent of one another. We could modify the code to read: if (x > y) cout << x << " is greater than " << y << endl; else cout << x << " is less than " << y << endl; if (x == y) cout << x << " equals " << y << endl; but this would still not solve the problem of having the second if appear to be independent of the rst if. Worse yet, it produces incorrect output. Can you gure out why? Any statement can be part of an if statement, and that includes if statements. Therefore, we could modify the original in the following way: if (x > y) cout << x << " else if (x < y) cout << x << " else // if (x == // (the last cout << x << "

is greater than " << y << endl; is less than " << y << endl; y) if is a comment because this is the only other possibility) equals " << y << endl;

This solves all of the problems mentioned above, and is a commonly used kind of C++ statement. This is referred to as a nested if because there is an if statement within an if statement. These can get confusing, but are extremely useful for some kinds of problems. For example, if you wanted to write a program to help cyclists decide which kind of ride to choose (rides in cycling are rated A-D for fastest to slowest), you would want to ask the cyclist how fast she normally rides (in mph): A rides for those who ride 18-22; B for 15-17; C for 10-14; and D for 5-9. The C++ code to reect this could be: 34

if (avspeed < 5) cout << "You should not ride with others until you can go faster"; else if (avspeed <= 9) // you know its > 5 or you wouldnt be here cout << "You will probably be most comfortable doing D rides"; else if (avspeed <= 14) // it has to be > 9 or you wouldnt be here cout << "You will probably be most comfortable doing C rides"; else if (avspeed <= 17) // it has to be > 14 or you wouldnt be here cout << "You will probably be most comfortable doing B rides"; else if (avspeed <= 22) // it has to be > 17 or you wouldnt be here cout << "You will probably be most comfortable doing A rides"; else cout << "Although you go faster than A rides, they are the best for you"; Notice that I do not need to check for a range at each if because the previous code does that for me. That is, if I get to the rst else that means that the cyclist does not ride less then 5 miles an hour (if the cyclist did, the program would have done the statement inside the if clause and skipped all of the other statements). Remember that with an if/else statement, the program will do exactly one of these statements. The rst if clause and last else clause are statements that check for out of range values. The chart is only from 5-22 mph, so if somebody enters a number outside that range, the program needs to account for this. When you write programs with nested if statements, be sure to check for out of range values. Lets write a nested if statement to determine what number corresponds to a given letter on the telephone. As you probably know, 2 has A, B, C; 3, D, E, F; 4, G, H, I; 5, J, K, L; 6, M, N, O; 7, P, R, S; 8, T, U, V; and 9, W, X, Y. Copy tele.cpp I have begun this if statement, but it is not nished. Fill in the missing blanks. Compile and test the program. 5.9.1 Testing

Now that you are beginning to write more complex programs, in which the statements that are executed will depend on the input that is given, it is especially important that you test your programs thoroughly. Test your programs so that each branch of each if statements is tested. This will often take many dierent executions of the same program.

5.10

Tracing Programs

In an earlier lab, we hand traced through a program to see how it would run. This is even more important to do when using complicated if statements. Lets look at a program grading.cpp, and trace through it. We will concentrate on the values of the variables and on the line that is currently executing. I have copied the entire program here for ease of reference throughout this section: 35

// Grading.cpp // This program calculates grades on a 10 point scale #include <iostream> using namespace std; int main () { char letter; int number; /* /* /* /* /* 1 2 3 4 5 */ */ */ */ */

// letter grade // number grade

/* 6 */ /* 7 */ /* /* /* /* /* /* /* /* /* /* /* 8 */ 9 */ 10*/ 11*/ 12*/ 13*/ 14*/ 15*/ 16*/ 17*/ 18*/

/* 19*/ /* 20*/ /* 21*/ /* 22*/ /* 23*/ /* 24*/ }

cout << "This program calculates letter grades for a given"; cout << " number grade." << endl; cout << "Enter a number grade> "; cin >> number; if (number < 0) { letter = e; // for error cout << "You must enter a positive number for your grade" << endl; } // end if (number < 0) else if (number < 60) letter = F; else if (number < 70) letter = D; else if (number < 80) letter = C; else if (number < 90) letter = B; else if (number <= 100) letter = A; else // grade > 100 not possible { letter = e; // for error cout << "Grade must be between 0 and 100" << endl; } // end else grade > 100 not possible if (letter != e) { cout << "A " << number << " is equivalent to a letter grade of "; cout << letter << "." << endl; } // end if (letter != e) return 0;

Copy the program grading.cpp. Compile it; test it; and look at it to see what it does. The numbers between the /* and */ are comments that will help us keep track of where we are 36

in the program. /* and */ is another way to write a comment. If we trace through the program, after the rst line of the program, the variables number and letter are both undened. Assuming that 87 is entered as the grade, after the fourth line, number contains the number 87, and letter still contains an undened value. After line 4, the next line executed is line 5, which checks to see if number is less than 0. If number were, then program execution would proceed to lines 6 and 7 and then line 21 since line 21 is the next statement after the nested if statement starting on line 5. Because the input is 87 and not a number less than 0, after line 5 is executed lines 6 and 7 are skipped; they are not executed. The next line to be executed is line 8. Answer the questions in the Tracing section of lab 5 which will continue tracing through this program.

5.11

Synthesis

Write a program that reports about 4 dierent programming teams: the Gazelles, the Grizzlys, the Gators, and the Giants. Your program will ask a person whether they want to know which team they play on (in which case they enter a W) or whether they would like to know which team they are playing next (in which case they enter an N). You will then ask the user for the rst initial of the users last name. You can assume that the user will enter a valid initial (i.e., a character between A and Z). If a W is entered for the choice, you will report which team they play on based on the users last name: If the rst initial of the name is A-F, the user will play on the Giants; if it is G-L, the user will play on the Gators; if it is M-P, the user will play on the Grizzlys; and if it is Q-Z, the user will play on the Gazelles. The function that does this reporting, NameTeam will simply print the one word answer corresponding to the appropriate team. If the choice is an N, you will use the users last name initial in the following way: If the rst initial of the name is A-F, the user will play the Gazelles next; if it is G-L, the user will play the Grizzlys; if it is M-P, the user will play the Gators; and if it is Q-Z, the user will play the Giants next. The function that does this reporting, NextOpponent will simply print the one word answer corresponding to the appropriate team. You may run your instructors program which shows you how your completed program should run. When you run the program, it will ask you to enter a W or an N to decide whether you want to know which team you are on, or what is your next opponent. Next, it will ask you to enter the initial of your last name. This must be a capital letter. Validation is not performed for this part (because I do not require it for your program). Run the program. Ask your instructor how to do this. Try running it with W and L, W and A, N and X, N and E and i (it wont ask you for an initial in this case). Make sure that it produces the correct answers. I have written the main function for this program. Copy the le teams.cpp. You only need to write the two functions NameTeam and NextOpponent You must declare (i.e., give a prototype for) and dene (i.e., write the function heading and body for) the two functions. Both functions are type void and both take one argument, a char which is the users 37

initial. For consistency, the only thing that these functions should do is output one word: the appropriate team name in the function. Finally save, compile, and test teams.cpp.

38

Laboratory 6
6.1 Objectives

Basic Loop Control

To be able to construct count-controlled loops. To be able to construct event-controlled loops. To be able to construct loops which perform common types of processing

6.2

Prerequisites

You should have studied the following topics in order to understand and complete the activities developed here: writing C++ programs in general and the if statement in particular.

6.3

Correspondence to Text

The material in this lab best corresponds to sections 5.1-5.2, 5.4-5.5 and part of 5.3 in the text.

6.4

Introduction

You have studied one control structure that allows you to write C++ programs in which not every statement in the program is executed sequentially: the if statement that allows you to write programs that make decisions about which statements to execute. There is one more general class of control structures: loops. Loops allow you to perform a statement more than one time (if the statement is a compound statement, you may perform a set of statements more than one time). Loops commonly have the format shown below initialize loop condition initialize loop processing while condition is true update processing update condition For example, consider the following loop which nds the maximum of n numbers: // assume that n is 3 /* 1 */ count = 1; /* 2 */ max = -9999; // a small number so all numbers // are greater than initial value /* 3 */ while (count <= n) { /* 4 */ cout << "Enter a number "; /* 5 */ cin >> number; 39

/* 6 */ /* 7 */ /* 8 */ }

if (number > max) max = number; count = count + 1;

This loop nds the maximum of three numbers. Copy max.cpp. Print, compile and run the program. Only the loop and loop initialization lines are numbered. This program starts by doing all of the lines up to the line marked line 3. At line 3, the program evaluates the logical expression inside the parentheses. If the expression evaluates to be true, the statement inside the loop (usually a compound statement) is performed; otherwise, the rst statement after the loop is performed (which in this case is a cout statement). It is possible that the statements inside the loop are never performed (if the logical expression initially evaluates to be false). The loop condition (the logical expression after the word while is called the loop condition) should be written so that it will become false at some point so that the loop is terminated at some point. Often, some statements before the loop are required to set up the loop. In this case, line 1 initializes the loop condition because it sets the variable count to 1. Thus line 1 performs the initialize loop condition function. The loop itself will perform some function for which initialization is also often needed. In this case, that is done by line 2 which initializes max to be some low number so that any other number will be greater than the number read in. Thus line 2 performs the initialize loop processing function. Within the loop, any number of operations may be performed. There is always, however, some statement(s) which perform the processing the reason that the loop is being done; in this case, that reason is to nd the max number. Line 5 updates the processing in this loop because it reads in number that is later (in the next line) used in processing. Line 8 updates the condition because it adds one to count which is used in the loop condition to determine whether the loop should be entered again. Answer the questions in section Loops in lab 6 questions. 6.4.1 Innite Loops

One common problem that people have is writing a loop that never ends. This is referred to as an innite loop. The way to avoid innite loops is to make sure that your loop condition comes closer to ending every time you go through the loop; that is, make sure the loop condition will eventually be false. For example, the loop in max.cpp comes closer to being false each time through the loop because count comes closer to being >= n each time. The rst time through the loop, count is 1, but it increases each time while n stays the same; thus eventually, it will no longer be the case that count <= n; the loop condition will be false; and the loop will stop. If you think that your program is in an innite loop, type ctrl-c to stop your program. On a UNIX system, you can try ctrl-d or ctrl-z if ctrl-c wont work; on other systems, ask your instructor what to do. 40

Copy max.cpp to noend.cpp Modify line 8 to be: /* 8 */ count = count - 1; (that is, change the + to a -). Now, count keeps getting smaller, so the loop condition gets further and further away from being false. This will result in an innite loop: count will start at 1, then be 0, then -1, then -2, etc. count will always be <= n, so the loop condition will always be true. Compile and run noend.cpp. Notice that the program asks you to enter a number forever. Thats because youre in an innite loop. Type ctrl-c to stop the program. 6.4.2 Writing Loops

Now consider the following code segment: /* /* /* /* 1 2 3 4 */ */ */ */ n = 3; count = 1; sum = 0; while (count <= n) { sum = sum + count; count = count + 1; } cout << "Sum is " << sum << endl; return 0;

/* 5 */ /* 6 */ /* 7 */ /* 8 */

This program sums the numbers from 1 to n. For example, n is 3 in this program, so the number 6 is output (1 + 2 + 3). This example shows two common processes performed in a loop: incrementing and summing. The rst, incrementing a number by 1, is so common that C++ has a special assignment form for it: variable-name++ Thus, instead of writing count = count + 1, I could have written instead: count++. A second common process performed in a loop is summing. A running total is kept of all numbers thus far. Again, this is so common that C++ has a special assignment statement for it: total-variable-name += summing-variable-name So, I could have written sum += count instead of sum = sum + count. These two statements perform the same function (the variables names of course can change if the sum is named something other than sum and the number incrementing the sum is some name other than count). The code above is in the le sum1.cpp. Copy that le into your directory. Answer the questions in section Sum1.cpp of lab 6 questions. 41

Modify the program to go through the loop 10 times. Compile and run the program. Answer the questions in section Sum 10 times of lab 6 questions. Copy sum1.cpp to sum2.cpp, and modify it to read in the number that the program should sum to. That is, instead of assigning the value of n within the program, you will read in the value of n. You should add the statement: cout << "Enter a number> "; You should then read in n instead of initializing n in the program. Compile and test the program using the number 5. Copy the program sum1.cpp to sum3.cpp. Modify the program to sum the numbers entered rather than summing the numbers from one to the number entered. For example, if you entered: 3 5 7 9 10 20 30 40 5 2 the sum would be 131 (3 + 5 + 7 + 9 + 10 + 20 + 30 + 40 + 5 + 2) (remember that you are summing 10 numbers for sum1.cpp. To do this, you will need to add one cin, one cout prompting message statement, and the variable declaration for the number that you are reading in. You will also need to modify the assignment statement that keeps a running total in sum. The extra cout should be: cout << "Enter a number> "; The extra cin should be: cin >> number; If you would like to see how this program should run, ask your instructor how to do that. Save, compile, and test sum3.cpp. 6.4.3 Event-Controlled Loops

The loop in max.cpp and sum1.cpp is called a count-controlled loop because the number of times that the loop is performed is some specic number (i.e., you can count the number of times that the loop will be executed). This is in contrast to an event-controlled loop in which the number of times that a loop will be performed is unknown; what stops the loop is that some event happens. For example, consider the loop below, which nds the max of numbers until 0 is entered. /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* /* /* /* 5 6 7 8 */ */ */ */ cout << "Enter a number "; cin >> number; max = -9999; // a small number so all numbers // are greater than initial value while (number != 0) { if (number > max) max = number; cout << "Enter a number "; cin >> number; }

42

This loop stops not after a certain number of times, but after the number 0 is entered. This 0 is called a sentinel, and this kind of loop is called a sentinel-controlled loop because numbers are entered until some sentinel is entered that stops the loop. There are several things to notice about sentinel-controlled loops. The condition initialization usually consists of reading in the variable that is in the loop condition immediately before the loop is reached so that there is some initial value when the loop condition is reached. This is performed in lines 1 and 2 above. The loop body itself usually consists of the processing (lines 5 and 6 in this case), and then the read for the next number (lines 7 and 8). So the last statement in a sentinel-controlled loop (and most other event-controlled loops) is generally a cin statement. This form is typical for most sentinel-controlled loops. Copy the le max2.cpp. It contains a program with the above loop. Compile and test this program. It nds the maximum of all numbers until a 0 is entered. 0 is not considered in computing the max. Test this by entering all negative numbers (but make sure theyre all greater than -9999). If 0 were considered, the maximum number would be 0, but it is not it is the largest negative number that you entered. Copy sum3.cpp to sum4.cpp. Modify sum4.cpp to add numbers until a -1 is entered. Do not add -1 in the summation. Save, compile, and test the program. 6.4.4 Validation Loops

One common use for a loop is to enter numbers until a valid number is entered. For example, in the Telephone program last week, if the user entered something other than A-Z (except Q), the program stopped and gave the user no information. A more helpful program would have told the user what the user had done wrong, and allowed the user to continue. Copy the program valtele.cpp. The section that last week was: if ((letter < A) || (letter > Y)) cout << "You must enter a letter between A and Y" << endl; is now : while ((letter < A) || (letter > Y)) { cout << "You must enter a letter between A and Y" << endl; cout << "Enter a letter "; cin >> letter; letter = toupper(letter); } 43

(I have also converted lower case letter to upper case letters so that if the user enters a lower case letter, that will be considered valid, and the number corresponding to that letter will be output just as it is for upper case letters). The point of the validation loop, as opposed to the if statement is that the loop is continually reentered as long as invalid data is entered. An if statement is only executed once; loops are executed as many times as the user enters invalid data. The rest of the program is the same as the version last week. Look at the source code and compile and run this program with valid and invalid numbers to ensure that you can write validation loops. Enter more than one invalid number during the same run. What happens?

6.5

Synthesis

Write a program to nd the product of two numbers that the user inputs, x and y, by adding x to itself y times. For example, if you wanted to nd the product of 3 and 4, you would add 3 + 3 + 3 + 3. This will only work if the second number is greater than or equal to 0 (though you could make it work if the second number were negative, we will not do that in this lab), so you must validate the second number to ensure that it is not a negative number. You will need to write two loops: one that validates the second input, and one that performs the multiplication. The multiplication loop should be a count-controlled loop similar to the sum.cpp programs. You should have a variable in which the sums are accumulated and a counter that starts at one; the loop should end when the counter is greater than or equal to the second term (so that the loop is entered exactly y times). Your program run should look like this for the numbers 4, -4, -20 and 20: This program multiplies two numbers together Enter the first number 4 Enter the second number -4 The second number must be nonnegative. Enter a number again, please -20 The second number must be nonnegative. Enter a number again, please 20 The product of 4 and 20 is 80 Write, compile, and test the program.

44

Laboratory 7
7.1 Objectives

Debugging

To be able to put debugging statements into a program. To be able to detect and correct logic errors in programs. To understand functions better.

7.2

Prerequisites

You should know how to write a loop before you start this lab.

7.3

Correspondence to Text

This lab does not correspond well to an exact section in the text, but sections 2.8 and 5.9 cover some of the material in this lab.

7.4

Introduction

So far we have written many programs, but if the program doesnt work the rst time, there has been no way to determine what went wrong. This lab will give you several hints for how to debug your programs, but the best method is still careful design and tracing through your program before beginning your coding.

7.5

Syntax Errors

The rst kind of error that you might run into when programming is syntax errors. Finding syntax errors is a matter of a lot of experience in some cases, but also paying close attention to syntax error messages. If the message says parse error, line 30 that means that somewhere on or before line 30 there is an error that prevented the compiler from translating the C++ code into object code. Lets look at a program full of syntax errors and try to gure out some of the problems. Copy lab7.cpp. lab7.cpp is the triathlon program that we wrote in one lab. Compile the program. You should get errors similar to the ones below: lab7.cpp:34: unterminated string or character constant lab7.cpp:23: possible real start of unterminated constant

45

This message usually means that you have forgotten an end quote. But the error message is also telling you that it found the end at line 34, but that it may have started at line 23. Find the missing end quote, x it, and recompile. Now, depending on what compiler you use, you might actually get more errors than you had before. This is because some errors mask other errors; that is, because the C++ compiler was looking for an end quote, it skipped over some of the other errors. You should see something similar to the following error list now: lab7.cpp: In function int main(...): lab7.cpp:22: no match for _IO_istream_withassign & << float & lab7.cpp:28: warning: implicit declaration of function int CalcLegTime(...) Lets look at the error messages one by one. The rst line, lab7.cpp: In function int main(...):, just means that the error messages listed below are in the function named main(). It is not an error message itself. The next error, no match for or no member function istream usually means that a << is backwards. The next warning message, implicit declaration, means that the compiler hasnt found a declaration for CalcLegTime. Why is this? Fix the error and warning messages by changing lines 14 and 22. Remember that C++ is case-sensitive. Compile the program again.

7.6

Debugging

Once you have successfully compiled your program, you are not through. Now, you need to get your program working correctly. If it doesnt, you need to nd out why. Your instructor may tell you about a debugger that you can use that will help you step through program. But even without a debugger, there are many steps you can take to nd where your program is going wrong. Often, adding debugging statements in the form of couts can help you pinpoint a problem. A debugging statement is simply a statement that allows you to output what the program is doing at a particular point, and where the program is. 7.6.1 Digression on Libraries

In order to see how some of this works, we are going to look at a relatively simple program, but one that seems complex because it uses three dierent les. A library consists of a header le and its associated function denition or implementation le. Header les as you know typically have declarations of constants and functions; the denitions for these functions (i.e., the function header and body) are in other (compiled) les. This header le has a .h extension; the implementation le has a .cpp extension. To see how the program that we will use works, run the program temp. Your instructor will tell you where to locate this le. 46

Run temp, and enter 32.0 and A to convert the temperature 32.0 degrees F to 0 degrees Celsius. Copy the les temp.cpp, heat.cpp, and heat.h. heat.h is a header le that contains the declarations for the function denition contained in heat.cpp. These two les together are a library the header le and the implementation. The heat.cpp must be compiled separately from your le (temp.cpp). This is so that the heat library can be reused by many programs (just as you reuse iostream and math in many programs). Compile the library into object code. Refer to the directions that your instructor gave you for lab 3 for how to do this. After compiling, you should have an object le possibly named heat.o which will remain in your library for linking (possibly with other object les) later. The process of linking will create an executable le. Compile and link your source with the heat.o le (or whatever your object le is called). Your instructor will tell you how to do this. You may then run your program; it should perform in the same way as the temp that you ran from the public directory. Run it and use the same values. You should get the same answers. 7.6.2 Finding the problem

This program looks as if it runs correctly. In fact, I took this program directly from a textbook (not yours), so one would think it would run correctly. However, there is an error in it. Your goal during this lab is to nd the error and x it. Test all of the options with the values 32.0F, 0.0C, and 273K. All of these should be equivalent. Test all six options and determine which are incorrect. Answer the questions under section Checking Conversion in lab 7 questions.

7.7
7.7.1

Debugging Statements
x is 0 debugging statements

There are many ways to nd an error. Tracing through a program by hand is one of the best. Weve done that before. In this lab, we will use cout statements to help debug programmatically (by adding statements in the program itself). It is also often useful to know exactly where you are in the program and the value of variables at a particular point in a program. This technique might help us debug the temperature program that we have. There are two conversions that are done incorrectly: C and D. There are several reasons that the output could be incorrect. One of the following must be true: The output could be incorrect because the input is read in incorrectly; because the values are being passed to the functions incorrectly; because the calculation in the function is being done incorrectly; because the value is being passed back from the function 47

incorrectly; or because the value is being corrupted after it has returned from the function. To check each of these, we will add cout statements at various points in the program to locate the problem. Copy the program heat.cpp to heat2.cpp and temp.cpp to temp2.cpp so that you do not destroy your old versions. This is a good debugging tip. When you start to debug a program, do not modify the original. Instead, copy it to another name, and modify the new le. Now, youll modify heat2.cpp and temp2.cpp until you nd the problem; then, youll go back and x heat.cpp and temp.cpp. Check the if clauses for c and d in the function Convert (in the le temp2.cpp) to ensure that the right functions are being called. Add a statement in these clauses to display the value of the returned expression. To do this, assign the value returned to a variable temp that you declare in the function Convert. Within the if clauses, you will need to add curly braces since youre making a compound statement. The code should now be: float Convert (float Temperature, char Conversion) { float temp; // for debugging purposes only Conversion = toupper(Conversion); if (Conversion == A) return FahrToCelsius(Temperature); else if (Conversion == B) return CelsiusToFahr(Temperature); else if (Conversion == C) { temp = FahrToKelvin(Temperature); cout << "C: Value returned is " << temp << endl; return temp; } else if (Conversion == D) { temp = KelvinToFahr(Temperature); cout << "D: Value returned is " << temp << endl; return temp; } else <rest of if statement> } This will display the value returned by the function so that you can check to see if its correct there, and if only later it becomes incorrect.

48

Next, edit heat2.cpp, the functions FahrToKelvin and KelvinToFahr. First, display the input value (maybe its not getting passed correctly). Next, display the value that is being returned (maybe the calculation is being done correctly, but its getting passed back incorrectly). Finally, lets perform the calculation in a dierent way (maybe the calculation is being done incorrectly). When converting from Kelvin to Fahrenheit temperatures, we could do the conversion from K to C then from C to F. Lets cout that also. So, the function KelvinToFahr in heat2.cpp should now be: { // the next statement ensures that the value was input and passed correctly cout << "The beginning value in Kelvin to Fahr is " << KTemp << endl; // next, print the value to be returned to ensure its correctness cout << "The ending value is " << (KTemp + 273) * 1.8 + 32.0 << endl; // the next 2 statements do the calculation differently as a check. cout << "The celsius equivalent is " << KelvinToCelsius (KTemp) << endl; cout << "The Fahr equivalent of that is " << CelsiusToFahr (KelvinToCelsius (KTemp)) << endl; return (KTemp + 273) * 1.8 + 32.0; // this was previously the only line } Modify the function KelvinToFahr and FahrToKelvin to include the couts specied above (you will need to modify the FahrToKelvin couts to display the Ftemp not the KTemp for the rst cout, and so on for the next three couts). In both cases, the return statement should not be modied. You will also need to add the statement: #include <iostream> // included only for debugging purposes at the beginning of the le heat2.cpp. This was not in the le before because no couts or cins occur in the program. Compile the program. Remember that heat2.cpp and temp2.cpp should be compiled together into one executable. Run the program. Use the value 32F and 273K to test the conversions (because we know what the answer should be for those values). When I did this, my output for the Kelvin to Fahrenheit conversion was: This program converts a temperature between the scales Fahrenheit, Celsius, and Kelvin. Please enter the temperature you wish to convert: 273 Please enter: A - to convert Fahrenheit to Celsius. B - to convert Celsius to Fahrenheit. 49

C D E F

to to to to

convert convert convert convert

Fahrenheit to Kelvin. Kelvin to Fahrenheit. Kelvin to Celsius. Celsius to Kelvin.

Your Choice ? D The beginning value in Kelvin to Fahr is 273 The ending value is 1014.8 The celsius equivalent is 0 The Fahr equivalent of that is 32 D: Value returned is 1014.8 -- The converted temperature is 1014.8. Your output may be slightly dierent because your couts may be slightly dierent. However, you should have the same numbers, and should be able to gure out the problem based on our debugging statements. Remember, there is a limited number of possibilities for the problem. It is one of the following: The output could be incorrect because the input is read in incorrectly; because the values are being passed to the functions incorrectly; because the calculation in the function is being done incorrectly; because the value is being passed back from the function incorrectly; or because the value is being corrupted after it has returned from the function. Weve checked each of these. Find out what the problem is, and x it. Go through the same process for testing the C option, converting Fahrenheit to Kelvin. Fix the Fahrenheit to Kelvin problem. Save the program, recompile it, and test it. Modify the original program, recompile it, and test it. In heat.cpp and temp.cpp (the original les), do not use any additional cout statements; we added these just to gure out where the problem was. Now that you know that, just x the problem.

7.8

Synthesis

Copy the program lab7end.cpp. Name it the same thing in your directory. All this program does is perform several calculations. The program reads in two numbers (x and y) and calculates: x2 x! + xy x! is read x factorial and is an abbreviated way of writing: x * (x-1) * (x-2) * (x-3) * ... * 2 * 1. For example, 4! = 4 * 3 * 2 * 1 = 24. Thus, if x were 4 and y were 5, the equation would be: 42 4! + 45 = 16 24 + 1024 = 1016. Copy the program to another name as well (e.g., lab7end2.cpp). There are two logic errors. There is nothing wrong with any of the variable types (i.e., all ints should remain ints and 50

all floats should remain floats). One note about types: this program has many variables of type double. This is simply a type of float. When you see it, think float (you can even change them all to floats if you want, and the program will still work correctly once youve xed the logic errors which have nothing to do with the types of the variables or the parameters). Add cout debugging statements to lab7end2.cpp, and compile and test lab7end2.cpp until you have found the errors. You might also nd it helpful to hand trace the program to nd the errors (getting a printout of the le may help you do this). Fix the errors and test lab7end2.cpp. Fix the errors in lab7end.cpp, and compile and test it. There should be no debugging statements in it, but the errors should be corrected.

51

Laboratory 8
8.1 Objectives

Review of Functions and Parameters

To understand functions and parameters better

8.2

Prerequisites

You should understand how to call, declare, and write functions.

8.3

Correspondence to Text

The material in this lab best corresponds to sections 3.3-3.5, 3.8 and 6.1-6.4 in the text.

8.4

Introduction

In this lab, we will practice using functions and parameters. There are two kinds of parameters: value and reference. Value parameters copy the value of the calling parameter to the called function. Reference parameters do not copy the actual parameters value, but instead create a pointer from the formal parameter to the actual parameter. Whenever the formal parameter appears on the left hand side of an assignment statement, the value that is changed is the actual parameter since the formal parameter points back to the actual parameter. Functions have types. The type of a function is its return value. Some functions have no return value; their type is void.

8.5

In, Out, In-Out Parameters

Parameters can be categorized into three classes: In, Out, and In-Out. For every parameter in every function that you write, you need to decide what kind of parameter you are dealing with. In parameters are those that have a value when the function is called. This value is used by the function, but it should not be changed when the function is done. Out parameters are those that whose initial value is not used by the function, but the parameter gain a value during the course of the function so that when control returns to the calling function, the actual parameters have dierent values than before the function was called. In-Out parameters are a combination of in and out parameters. The value of the parameter when the function begins is used in the function, and the value of the parameter is changed during the function so that when control passes back to the caller, the actual parameters have dierent values than before the function was called. Answer the questions In and Out in lab 8.

52

8.6

Value Parameters

You want to use a value parameter when the parameter is going to be used, but not modied during the function. For example, you might want to print a series of values. Copy printvals.cpp. This function is a variant of the intocm.cpp program that you have already seen. Add the function prototype and function header for writeanswer. This function prints out the answer. The actual parameters, inches and centimeters are value parameters because their value does not change as part of the function, but the values are used in the function. Copy cube1.cpp. This program cubes a number, and prints the result inside a function. Write the function call, the function header, and the function prototype for this function. This function is not as natural as the previous function. It both calculates and prints the cube. In general, functions that perform two tasks probably need to be broken up. Finally, lets try to use a value parameter in a function that reads in a valid value. Copy read.cpp. Write the function call, the function header, and the function prototype for this function. Use value parameters. Test your program. Does it work? Why not? Answer questions read.cpp in lab 8.

8.7

Reference parameters

As you can see from the previous section, value parameters prevent functions from returning values in their parameters. When it is necessary to modify an actual parameter, the parameter must be passed by reference. To do this, place an & before the parameter in the function prototype and in the function header. Reference parameters do not change the way functions are handled within the function or in the function call. Modify read.cpp so that it works correctly. Compile and test the program. Copy swap.cpp. This program swaps two values. That is, the parameters in function swap both start with values, and their values are changed in the function. Answer questions swap question in lab 8. Write the function call, the function header, and the function prototype for this function. Compile and test the program.

53

Lets try one more. Copy program sortc.cpp. This program sorts three input characters. If c, a, b is entered, a, b, c will be output. Answer questions sort question in lab 8. Write the function call, the function header, and the function prototype for sort, and the function header and prototype for function swap.

8.8

Return values

We have not dealt yet in this lab with functions that return values. Instead of sorting three characters, maybe we would like to nd only the minimum value for the three. We could use sortc.cpp to do that, but it does more than we need. Copy min.cpp. Notice that rather than returning the minimum value in a parameter, we now return the minimum value in the function itself. Whenever a calculation is being performed, it is a good idea to return the result of that calculation in the return statement of the function. Write the function prototype, the return statements and the function header for findmin. Compile and test the program. Answer questions findmin question in lab 8. Earlier in the lab, you wrote a cube function, but I mentioned that it wasnt the best way to write a function because it did two things, a calculation and a print. Copy cube2.cpp. This version is much nicer because the function cube does only one thing: the calculation. Write the function prototype, the function body (including the return statement), and the function header for the function cube. Compile and test your program. Functions are often used as conditions in if and while statements: when a 1 is returned, the condition is true; a return value of 0 indicates the condition is false (sometimes, these functions return values of type bool rather than int). Copy the program iswheel.cpp. This program has a function that determines if the character entered is one of the Wheel of Fortune letters: r, s, t, l, n, or e. I have made the input an upper case letter to simplify the function. Write the function prototype, the rest of the function body including the return statement, and the function header for iswheelletter. Compile and test your program.

54

8.9

Synthesis

Write a program that calculates how much a tuition hike would cost. I have written the main program for you. You only need to write the program prototypes, return statements, some calls and the function headers. Copy program tuition.cpp. Write all of the function prototypes and headers, and write the calls for getincrease and getcurrenttuition, and the return statement for calcincrease. Compile and test the program.

55

Laboratory 9
9.1 Objectives

I/O Streams

To be able to use input and output streams more eectively To be able to read and write to les using fstreams

9.2

Prerequisites

You should know how to use cout and cin to do this lab. You should also understand value and reference parameters.

9.3

Correspondence to Text

The material in this lab best corresponds to sections 8.1-8.6 in the text.

9.4

Introduction

This lab allows you to explore the output and input streams in more depth, and gives you some experience reading and writing from les.

9.5

Streams in Files

Any collection of characters in C++ can be considered a stream of characters. C++ allows us to read and write les without having to use redirection (this is helpful especially if more than one le is being read from or written to). 9.5.1 Looking at le streams

Copy the le countvowels.cpp. This program contains function parameters that are passed by reference. You will not need to use this information directly in this lab, but you should understand how these parameters dier from value parameters. This is a relatively complicated program. It may help you to print it out. The main function is what I want to talk about rst. First, notice that there are two strings, named infilename and outfilename. We have used strings before, but you may never have initialized them. C++ allows you to initialize many objects (that is, variables that are a type of class as opposed to a built-in type like ints) by putting the initial value(s) in parentheses; this is actually a function call as you will see later in the semester. For now, all you have to understand is that two objects have been created: infilename has the initial value "infile";

56

outfilename has the initial value "outfile". It is possible to change initial values, but we wont do that in this program. Getting back to le handling: In order to read and write les from a le instead of the reading from the keyboard and writing to the screen, ve parts of the program must be modied. 1. You must include fstream which allows you to read and write le streams. 2. You must declare as variables any input or output le streams that you are going to use. To do this, use the type ifstream for any input les (i.e., les from which you will read only), and type ofstream for any output les (le that you will write to). Look at program countvowels.cpp to see how I did this in main. 3. You must open any les that you are going to use. To do this, you will use the name of the le variable, a dot (period), and the name of the function (i.e., open in this case). The function open requires one argument: the name of the le on disk. So, if the le variable is outfile, an ofstream or output le, and the name of the disk le to be created is vowelcount, you would open the le with the line: outfile.open("vowelcount"); This would create a le name vowelcount, and write all information that is written to the variable outfile to this le. Similarly, to open a le for reading, if the ifstream variable name is infile and the name of the le on disk is infile (note that le and variables names do not need to be the same name, but they can be), you would open the le with: infile.open("infile"); 4. The les can be read from and written to just as cin and cout are read from and written to. To read a character into a variable named inchar from a le with the ifstream variable name of infile, type: infile >> inchar; just as you would have typed cin >> inchar to read from the keyboard into character variable inchar. Only the stream name is changed. Similarly, writing to le streams is accomplished just as writing to the output stream cout is accomplished. Examples of reading from and writing to le streams are in function reportvowels. Notice that when le streams are passed as parameters, they are always passed by reference. 5. Close all les. This is done just as opening les is done except that the function name is close instead of open, and there are no arguments required. An example of this is in the function main.

57

6. Most input les (les that are being read) have an end of le check. In countvowels.cpp, the end of le check (in function reportvowels), is: while (!infile.eof ()) This is typical. This while statements says to keep reading until the end of le marker is read. Once the end of le marker has been read, the condition in the while loop becomes false (0) and the loop is exited. The format for an end of le check is: <ifstream-variable-name>.eof() (with no arguments). Do the questions section Files in lab 9. Copy the le infile, and compile and test countvowels.cpp to see what it does (infile is an input le for countvowels.cpp). Look at the output le to see what it looks like. 9.5.2 Writing your own program

Write a program to count the number of letters, and the number of digits from an input le incounts Write the output to a le named: outcounts. I have started the program for you. It is called lab9.cpp. Copy it into your account. Copy the le lab9.cpp into your directory. Add the ve parts for each le (the include le, the variable declarations, the opening, the reading and writing, and the closing). You will also need to add an eof le check. Look at countvowels.cpp to see how that is done. The le is commented with where these should go. Copy the le incounts. Save and compile and test the program. This will create an output le outcounts.

9.6

Getting precise about how data is read and written

Sometimes, you might want to format your output or read your input dierently than C++ does automatically. C++ has several ways that you can do this. It is not necessary for you to memorize all of the dierent options because there are many. Output ags are discussed in Section 5.3 and Section 8.5. There are also ags for input that specify the way that input will be read. These are briey discussed in Section 8.5, and you will come across them as you need them. 9.6.1 Input options

Lets briey examine some of the functions that we have already seen: open, close, eof, and fail. You will learn why the dot notation is used later on in the semester, but the general form 58

of stream functions is: streamname.functioname(argstofunctions) For example, checking for eof for an ifstream variable ins would be: ins.eof() because there are no arguments for this function. Some of these functions are listed on page 392 of your book. The function open prepares an ifstream le for reading and an ofstream le for writing. The function close closes the le associated with the streamname. The function eof checks to see if the last character read was an end of le marker; and the function fail checks to see if the last operation performed with the specied stream was a failure (in which case 1 or non-zero is returned) or a success (in which case 0 is returned). 9.6.2 get

There are two functions listed on those pages that we havent talked about: get and put. Ill only talk about get. When input is read using the input stream operator (>>), all spaces are skipped. Given the C++ code and input below, no matter how many spaces are in front of the a, a is the character that will be placed in firstchar because white space is skipped when reading characters. code: char firstchar; cin >> firstchar; input: abcd23erf67a<nwln> Do questions section Input in lab 9. When reading characters, you will often want to read all characters including any white space (white space is spaces, tabs, newlines, and carriage returns). Because the input stream operator skips over white space, you will need to use the function get for reading characters. To see how the get function is used, lets examine a program which counts the number of vowels and spaces from the le inputfile, and outputs to outputfile the number of each vowel and how many spaces there were. For example, if the input le is: A very short file. the output would be: Number of As: 1 59

Number of Es: 2 Number of Is: 1 Number of Os: 1 Number of Us: 0 Number of spaces: 1 Total number of Lines: 3 Because this program counts the number of spaces in a program, spaces cannot be skipped as part of reading the input. Copy the program vowelsagain.cpp. This program runs just as described above. Examine the program. Notice two pieces of the code in particular: The get function call on lines 72 and 83 (in function processfiles). This is how to read a le using get instead of the input stream operator. The end of line loop check also includes a check for end of le (this is on line 74 in the function processfiles). This is to catch les that do not have a newline at the end of the last line, but that just have an end of le marker. You might want to print out this le to help you with the next section.

9.7

Synthesis

Write a program count.cpp that counts the number of spaces in a le infile, and the number of lines in a le. Write the answer to a le outfile. The format of the answer should be: Total Number of Spaces: 7 Total Number of Lines: 3 Save, compile, and test the program.

60

Laboratory 10
10.1 Objectives

Arrays

To be able to declare and process arrays

10.2

Prerequisites

You should thoroughly understand loops in order to be able to do this lab.

10.3

Correspondence to Text

The material in this lab best corresponds to sections 5.3, 7.2, 9.1-9.3 in the text.

10.4

Introduction

Up until now, all of the variables that we have used have been stored in single boxes unrelated to each other. This prevents us from doing many kinds of processing. For example, one common problem in Computer Science is sorting a list of numbers (e.g., to print out a list of all grades of all students by student ID). It is relatively easy to sort two numbers. If the variables x and y hold two numbers, the following code will print out a sorted list of x and y: if (x < y) cout << "The sorted list is " << x << y; else cout << "The sorted list is " << y << x; If we have 3 numbers, x, y, and z, we could do the following: if (y < x) swap(x, if (z < x) cout << else if (z cout << else cout <<

y); "The sorted list is " << z << x << y; > y) "The sorted list is " << x << y << z; "The sorted list is " << x << z << y;

This code assumes that a function swap exists which swaps the values of the arguments (e.g., if x = 4 and y = 2, after swap(x,y), x = 2 and y = 4). To sort 4 numbers, x, y, z, and w, we could do the following: if (y < x) swap(x, y); 61

if (z < y) { if (z < x) { swap(x, z); if (z < y) swap(y, z); } else swap(y, z); } if (w < z) { if (w < x) cout << "The sorted list else if (w < y) cout << "The sorted list else cout << "The sorted list } else cout << "The sorted list is

is " << w << x << y << z; is " is " << x << w << y << z; << x << y << w << z;

"

<< x << y

<< z << w;

And after that, life gets complicated! One problem is that there is really no way to make this comparison process repetitive or to simplify it in any way. Another problem is that many times programs that sort numbers (e.g., student numbers for reports each semester) may have a dierent number of numbers each time they run the program (i.e., there may be 8000 students in the Fall, but only 6000 in the Spring). What do we do with those 2000 extra variables that arent being used? We cant just input the number of students (6000 or 8000), and then tell the program to ignore 2000 variables how would we do that? Also, its going to be a pain declaring 8000 dierent variables to hold the student numbers. There are other problems, too. The bottom line is that we need something else that will allow us to hold chunks of similar data in one place, and that will allow us to do some loop processing with these chunks of data. The mechanism for this something else is called an array. 10.4.1 What is an array?

An array is a collection of a xed number of values all of the same type. You might think of an array as a collection of identical drawers in a dresser (that are very small because they can only hold one item); each drawer can hold the same type of item and the drawers can be identied as the rst drawer, the second drawer, etc. The identication of a drawer is called its index. Indices in C++ start with 0. So the rst drawer (or slot) in an array is indexed by 0. Because the index is 0, many people refer to this as the zeroth drawer. If there are 10 drawers, the indices will range from 0 to 9, with the last 62

drawer having an index of 9. The item inside the drawer or slot is referred to as an element. For example, an array of ten numbers might look like the box below; the elements are in the boxes and the indices are beneath the boxes (this is a typical way to represent an array): 3.2 0 -234.3 1 23.0 2 2434.234 3 3.2 4 -2.0 5 89.9 6 12.0 7 67.22 8 66.0 9

In the above array, there are ten elements that are of type float and ten indices (of type int). The element that corresponds to index 4 is 3.2; the index corresponding to the element 66.0 is 9.

10.5

Declaring arrays

To declare the above array, and name it sample, you would use the following: float sample[10]; This would declare an array named sample that has ten slots that will be lled with values of type float, and the indices for this array will range from 0 to 9. Suppose that we wanted to do the count vowel processing that we did last lab, but we dont want to discard every character after it is read (we may want to do other processing with the characters). We could declare an array of characters that would hold one line of data (one line is 80 characters): char line[80]; This would declare an array named line that would hold 80 characters in index 0 through index 79, all of type char. Answer questions Declaring Arrays in lab 10.

10.6

Referencing Array elements

Now that we know how to declare arrays, we need to know how to get the value of an element stored at a particular index. This is done by subscripts, written in square brackets. To refer to the element in the index 2 of the array sample, we would write: sample[2] To refer to this element, we would say (in words) sample sub two. Just as referring to variables can be used for storing or for retrieving elements, array referencing can be used to store elements in index 2 or to get the value of the array element stored at index 2. For example, sample[2] = sample[3]; 63

would store the value 2434.234 (sample[3]) in the slot indexed by 2 (sample[2], the slot that previously held 23.0). Variables may be used in subscripting as well. If i is an int with the value 3, sample[i] = sample[i+2]; would store the value -2.0 (i + 2 = 5, sample[5] = -2.0) in the slot indexed by 3 (where 2434.234 used to be). The array would then look like this (including the earlier change assigning sample[3] to sample[2]): 3.2 0 -234.3 1 2434.234 2 -2.0 3 3.2 4 -2.0 5 89.9 6 12.0 7 67.22 8 66.0 9

sample[3*i] = sample[10] would result in an error since sample[10] is outside the range of the array (sample[3*i] is ne; if i=3, sample[9] would be the slot referenced). The C++ compiler will not give you an error, but you will get unpredictable results if you try to access values outside the range of the array. Do questions Referencing Arrays in lab 10.

10.7

For loops

for loops are commonly used with array processing. for statements are a variant of the while statement. They have an initialization, a condition, and an increment all separated by semicolons and all in parentheses. The initialization of the loop is done rst, then the condition. If the condition evaluates to true, the loop is entered; otherwise, the loop is skipped. If the loop is entered the statements in the loop are performed, and then the increment statement. After the increment, control passes back to the comparison. If the comparison evaluates to true, the loop is entered again; otherwise, control passes to the rst statement past the for statement. The following two loops are equivalent; both print out the numbers 0-9: i = 0; while (i < 10) { cout << i; i++; }

for (i=0; i<10; i++) cout << i;

10.7.1

Initializing an array

One big advantage of arrays is that you can do something to each element within a loop. For example, you might want to initialize all of the elements in sample to 0. The following loop will do that: 64

for (i=0; i<10; i++) sample[i] = 0.0; This loop initializes i to be 0 (the rst part of the parenthesized for expression). It then performs the i < 10 comparison (the second part of the parenthesized for expression). i (which is 0 at this point) is less than 10, so the for loop body is entered. The body assigns 0.0 to sample[i] (which is at this point sample[0]). Thus the array looks like this after the rst time through the loop: 0.0 0 ? 1 ? 2 ? 3 ? 4 ? 5 ? 6 ? 7 ? 8 ? 9

The loop completes, and goes up to the top of the for, and increments i by 1 (the third part of the parenthesized for expression: i++). i is now 1. Next, the i < 10 comparison is performed again: (the second part of the parenthesized for expression). Since i (now 1) is less than 10, the loop is entered again. sample[i] again gets the value 0, but this time sample[i] is sample[1]. The array now looks like: 0.0 0 0.0 1 ? 2 ? 3 ? 4 ? 5 ? 6 ? 7 ? 8 ? 9

The loop continues until all elements in the array from index 0 to index 9 have been assigned the value 0.0. Copy the program lab10.cpp. This program should read in ten numbers into the array sample and write them out. Add the for and cout statements to write out the numbers (you can look at the cin statement as an example). Save and compile the program. Copy the le testlab10. Test lab10.cpp redirecting the input from testlab10.

10.8

Casting

And now for something completely dierent . . . Data in C++ is typed, but that type may be changed. There are many reasons for doing this; I just wanted to introduce the notion to you in this lab. As you know, characters are stored in ASCII format on most machines. That means that an A inside the machine is stored as a 65 (or, actually, the binary equivalent: 1000001). It is possible to treat this A as an integer and not as a character (that is, look at the 65 and not at the A interpretation). To do this, you can coerce the character into being an integer. We have actually done this before. Whenever we add two numbers and one is of type float and one is of type int, the int is coerced into being a float type so that the two can be added. This is called coercion. 65

The following code illustrates this: int x; float y, z; x = 3; y = 23.345; z = x + y; Here, x is being implicitly coerced into being a float. It is possible to do this explicitly as well. Below are the three ways to do this: // Method 1 int x; float y, z; x = 3; y = 23.345; z = (float) x + y; // Method 2 int x; float y, z; x = 3; y = 23.345; z = float(x) + y; // Method 3 int x; float y, z; x = 3; y = 23.345; z = static_cast<float> (x) + y; All three of these do the exact same operation; there are just dierent ways of doing it. Explicit casting is preferred to implicit coercion because it is easier to see what is happening to a person reading the program. In Standard C++, the last method is preferred over the other two because it explains the type of cast being performed. Answer questions Coercion in lab 10.

66

10.9

Synthesis

Write a program that reads in 10 real numbers (floats), multiplies each by 10, and outputs the results. Name the program add10.cpp Use an array to store the 10 numbers as you read them in. Compile your program. Test your program using the le testlab10. Redirecting the input from testlab10, the output should look like: Enter 10 numbers> The array elements multiplied by 10 are: 123 -342 0 2345 122 456784 -234 -33580 19864 3780 If you have extra time, you might try adding up all of the numbers in the array and output the total sum also. For example, if you redirect the input from testlab10 again, the output would be: Enter 10 numbers> The array elements multiplied by 10 are: 123 -342 0 2345 122 456784 -234 -33580 19864 3870 Total Sum: 448952

67

Laboratory 11
11.1 Objectives

Testing and Debugging

To know how to test a program thoroughly

11.2

Prerequisites

You should understand looping and selection before doing this lab.

11.3

Correspondence to Text

The material for this lab does not correspond to a single portion of the text; the closest correspondence is section 6.5.

11.4
11.4.1

Introduction
Testing

You have done one lab on debugging, and we talked a little bit about testing, but this lab will cover testing in more depth. Testing your program is just running your program, and checking the output to ensure that it is correct. In order to test a program, you must know what the output should look like, and then you must run the program and check the output that is produced. There are two ways to test a program: testing the program without looking at the code (this is called black-box testing) and testing the program using the code to develop your test data (white-box or glass-box) testing. This lab will talk about the last: developing your test data based on your code. Both kinds of testing should be performed. Black-box testing is usually done by a person other than the programmer. The important part about testing is to do it, and to take testing very seriously. Develop test data on your own that will thoroughly test all parts of your program so that no matter what data is actually run through your program, your program will function appropriately.

11.5
11.5.1

Testing
Selection statements

If all C++ code executed sequentially, testing would be not be a dicult task because all the code in a program would be tested every time the program was run. However, loops and selection statements make life more dicult. An if statement in a program means that either the if is done or the else is done (or, if there is no else, then the if is skipped; in both cases, there are two possibilities). This means that to test every line of code in a program that has an if statement, you must run the program at least twice. Consider the following code: 68

if (x < 10) digits = 1; else digits = 2; if (y > 32) bits = 6; else bits = 5; In this code, there are 4 (22 ) possibilities: x could be: y could be: < 10 OR > 32 OR 10 10

1. x could be < 10 and y could be > 32 2. x could be < 10 and y could be 32 3. x could be 10 and y could be > 32 4. x could be 10 and y could be 32 Thus, in order to fully test the above code, at least four dierent tests would have to be run, a test where: 1. x is < 10 and y is > 32 2. x is < 10 and y is 32 3. x is 10 and y is > 32 4. x is 10 and y is 32 That is the only way to test every line of code. In general, 2n dierent runs of a program will test n if statements. If there are more if statements, testing can get very elaborate. Answer questions How many tests? in lab 11. 2n tests is a lot of tests for n if statements. Although you may be able to actually test this many conditions in your own programs, it is impossible for large programs, especially when we start adding loops. In general, the goal of testing is to test as much of the code as is reasonable with the most varied data. 11.5.2 Loop Testing

When testing loops, it is important to remember that loops may be entered 0 times; programs should handle this situation appropriately. In general, your test data should test going through a loop 0, 1, and more than 1 times; that is, you should perform three dierent tests for each loop. 69

Another general rule in testing is that you would like to test as much of the code as possible in each test, but there are very few programs (except trivial ones) for which one test is sucient to thoroughly test the program. Copy the program testing.cpp. This program nds the max of a series of numbers that you enter. The program stops when you enter a 0. Examine the program and develop test data for it. Remember that you will need to run the program multiple times in order to thoroughly test it. Answer the questions Test Data for lab11 under lab 11. 11.5.3 Testing Boundary Conditions

For conditions that use relational operators, it is a good idea to test equals, less than, and greater than. For example, in the following code (repeated from above), if (x < 10) digits = 1; else digits = 2; one would want to test x==10, x<10, and x>10. x==10 in this case is a boundary condition because it is on the boundary of the condition. Both loops and selection statements have boundary conditions. Answer the questions Boundary Conditions in lab 11. Boundary conditions should be tested as much as possible. 11.5.4 Testing Functions

Successful testing of functions will drastically reduce your overall testing and debugging time. Functions should be tested individually, before they are combined as a program. Remember the program in which you tried to nd out what was wrong with the program by testing it as a whole? It would have been much easier if we had tested each individual function. Programs can be written and tested using a combination of both bottom up (writing and testing individual functions), and top-down (writing and testing the program as a whole) design. Copy the les func1.cpp func2.cpp, func3.cpp, func4.cpp, func.h, and drv1.cpp from the public directory. The goal of this exercise is to write programs that will test each of these functions. A program whose sole purpose is to test a function is called a driver. In order to test each of these functions, well write programs that do nothing except call a function (func1, 2, 3, and 4) and ensure that it performs correctly. There will be a total of 4 drivers (programs). The rst will test func1, the second func2, and so on. 70

In order to write the drivers, we must rst determine what each function does. func1 has two input/output parameters parameters that have a value when the function starts, and that are modied within the function. We can tell this from the comments and from reading the C++ code. Thus, a driver function for func1 must provide input values for the function, and evaluate the changes that are made to those values. In general, the main function (the driver) will dier depending on the function. Look at the parameters of the function to determine what the driver should do. If the function has input parameters, then the driver should give these parameters values before calling the function. (probably by reading in values), and pass those values to the function in order to see whether they will be correctly manipulated. If the function outputs some value, then the driver should print out the value of the output parameter to ensure that the calculation was done correctly. The function in func1.cpp has two parameters, both of which are input/output parameters, so the driver needs to provide input to the function, and then write out the modied parameters. That is, the driver for func1 should read in two values, and print out the values after the function call since the values have changed. A program to test func1.cpp has been written for you; it is called drv1.cpp. To test func1, compile it with drv1.cpp, and run it. Do questions Designing Drivers in lab 11. Develop a driver for func2, for func3, and for func4. You will write three separate programs. Name the programs drv2.cpp, drv3.cpp, and drv4.cpp Each of these drivers should have an include "func.h" statement, but you should not need any other include statements. func.h denes arraysize so you do not need to dene it in any of your drivers or in any of your functions (as long as you include func.h). In order to develop the drivers, you will need to look at the functions, see what the parameters are (input, input/output, or output), and develop the drivers based on the functions. For example, with the swap function, it exchanged two values that already existed, so the driver read in those values, and printed them out after the function had been called. (Hint: You may want to use func4.cpp as part of the driver when testing func2.cpp, and the reverse.) NOTE func3.cpp uses the function from func1.cpp, so when you compile this function, you will need to include func1.cpp when compiling. Compile, develop test data for, and test these three programs.

11.6

Synthesis

No synthesis this week!

71

Laboratory 12
12.1 Objectives

More Arrays

To understand reference parameters thoroughly To be able to process arrays using loops To be able to perform common functions of an array to be able to process only part of an array

12.2

Prerequisites

You should thoroughly understand for loops in order to be able to do this lab.

12.3

Correspondence to Text

The material in this lab best corresponds to sections 9.3-9.5 in the text.

12.4

Introduction

In this lab, you will get more practice on arrays, see some common functions (e.g., nding max or min) that are performed on arrays, and get some extra practice with reference parameters.

12.5

Review of Reference Parameters

You have already learned the dierence between value and reference parameters, but this lab requires that you thoroughly understand them, so well review a little. Reference parameters access the variable in the calling function; they are actually pointers back to the value of the actual parameter in the calling function. Whenever the value of a reference parameter is changed, the value of the variable (that is the actual parameter in the calling function) is changed as well. Value parameters copy the value of an actual parameter into the parameter in the function. Whenever the value of a value parameter is changed, the value of the variable (that is the actual parameter in the calling function) is not aected. Answer questions Reference Parameters in lab 12. 12.5.1 Syntactic Dierence

For simple variables (i.e., variables that are not array variables), in order to pass a variable by reference, you use the & symbol; in order to pass a variable by value, you use nothing. Thus a prototype for a function swap that has two char parameters, both passed by reference is: void swap(char&, char&); 72

The function header for this function is: void swap(char &x, char &y) When the parameters (x and y in this case) are used in the calling and called function, both reference and value parameters are treated in exactly the same manner. The only thing dierent about reference and value parameters syntactically is the way that the parameters are passed. There is no way to tell when looking at a parameters use in an executable statement whether it is a variable, a value parameter, or a reference parameter. Array parameters are passed by reference even though you do not use the & in the parameter list. This avoids copying the array into each function that the parameter is passed to. 12.5.2 When to use which

The following table describes some situations when you would want so use value and reference parameters.
Function Purpose Find Max of 2 Numbers Find Max of Numbers in Array Parameters 2 Numbers 1 Array Returns Max of 2 Max nbr Pass by value value reference Why? Numbers are not modied Always pass by value if possible Arrays always passed by reference Use const since array is unchanged (see Section 12.6.1) Since 2nd number is changed (it will contain Max on return), it must be reference. Although the second number may be Max initially, because it might change, it must be passed as a reference parameter.

Place Max of 2 nbrs in 2nd Nbr

2 Numbers

None

value reference

After you understand the table, answer When to Use Which in lab 12 questions.

12.6

Typical Array Problems

This section looks at some typical array processing. Make sure that you fully understand all of these loops and what they do. These examples also demonstrate how to pass entire arrays and how to pass elements of an array. Entire arrays are always passed by reference, but individual elements are passed by value or by reference depending on their purpose in the function. To pass an entire array, use the variable name for the array with no indices. To pass individual elements of the array, use the name of the array with the appropriate index. For example, assume that we have an array sample. Also assume that there is a function, sortels which requires the rst element of an array as its rst parameter, and the entire array as its second. To call sortels with the rst element of sample and the entire array sample would be: sortels(sample[0], sample)

73

12.6.1

Finding the minimum element

Copy the program min12.cpp. This program prints the minimum element in sample. It is similar to lab11.cpp in the way that the array is read and written, but these processes have been moved to functions. As a result, the const declaration that declares the size of the array has been moved outside the scope of main so that it is global and can be accessed by all functions. One other detail to notice is that the array is passed as a const parameter when it will not be modied by the function. All arrays are passed by reference rather than by value, but if the array is not going to be modied in the function, it is a good idea to use a const before the argument. To do this, write const before the formal parameter in both the function prototype, and in the function denition (the function header). This has been done for writearray because this function does not modify the array; readarray on the other hand does modify the values in the array, so it is not passed as a const parameter. Add the required read statements, and compile and test it. Copy min12.cpp to max.cpp, and modify it so that it nds the maximum element. Again, do not change any output statements, except anywhere where the old program has the word minimum, your program should have the word maximum (and you may want to change the names of the variables that are output). Test the program with testlab10 (if you have deleted testlab10, you can copy it again). 12.6.2 Reversing elements in an array

Copy program reverse.cpp. This program reverses the elements in an array. Look at function reversearray. The array is reversed by swapping element 1 with the last element, element 2 with the next to last element, etc. The loop only goes half way through the array. Why is this? Notice how the array elements are passed to the function swap. Because the value of the array elements are modied, the array elements are passed by reference. But there is nothing dierent about passing array elements than passing simple variables. The swap function would be identical whether array elements or simple variables were passed. There are several tricky parts about the read function as well. Look at readarray. This is the rst time weve used an array where we may use less than the entire array. The largest word that this program can handle is 10, but we might enter a word with less then ten characters (e.g., word or dad). We would like the program to function correctly, but if we reversed word in an array of ten characters and used all of them the reversed array would be: ? ? ? ? ? ? d r o w 0 1 2 3 4 5 6 7 8 9 instead of what wed like:

74

d r o w ? ? ? ? ? ? 0 1 2 3 4 5 6 7 8 9 Therefore, when reading in values where we might not need to use the entire array, we often return a length value that is used in subsequent functions. In this case, we determine the length of the array by looking for a return character. We need to use the function cin.get because cin skips over white space, and the return character is considered white space. The word is read, and then the length of the word is passed to the other functions in the program. Finally, the last if statement increments i by one if we went up to the maximum number of characters. This is because when were doing the comparison in the for statement, were actually reading one character too many and then throwing it away for any word that is less than the maximum number of characters. That is, if we read word\n (5 characters) what we want in the array is word (4 characters) so the length that we return is the length before we read \n. But if we read 10 characters before the \n, then the rst ten characters will be treated as the word, and we dont want to throw any of those away because we havent read an extra \n. In order to not throw away an extra character if we read the maximum number of characters, we increment i (to arraysize + 1) so that when i is decremented in the return statement (throwing away the \n), it will decrement i back to where it should be (to arraysize). If you do not understand this subtlety fully, it will not hinder your ability to do the rest of the lab; I explained it only for those who are interested in the details of this function. Compile and test the program using the string word and using the string dad Because the string dad is the same backwards and forwards, you will not see any dierence when the array is reversed. dad is called a palindrome because it is the same backwards and forwards. Lets write a program to determine if a word is a palindrome. There are many ways to do this, but one way is to copy an array to another array, reverse the second array, and then compare the two. If the forwards and reversed arrays are equal, the word is a palindrome; otherwise it isnt. Copy program pal.cpp. This program is similar to reverse.cpp, but an extra function has been added: ispalindrome. ispalindrome has three functions, two of which you need to write: copyarray which should copy the rst array to the second, and equalarrays which should compare the individual elements of the two arrays. The function ispalindrome is written, and should not be modied; just write the two functions declared in ispalindrome that are not already written. Both of these process only length number of elements; they should not examine the entire array. Write the two functions copyarray and equalarrays. Do not add any couts to the program. copyarray should simply assign all of the elements from 0 to length to the second array; equalarrays should check the elements from 0 to length, and return 1 if the arrays are equal; 0 if they are not. Save, compile, and test your program. 75

12.7

Synthesis

Write a program to calculate your grade average for your labs, and print the highest and lowest grade. Use an array to hold your 14 lab grades; the array elements should be of type float. Print out your highest, lowest, and average grades. I have written the cout statements for you as part of a main function. Because we want to be able to keep a running average, it should be possible to enter less than 14 grades (e.g., this week, you would enter 12 because there will be 12 labs that you have completed). Copy the program labgrades.cpp. Write the functions for this program (I have written readgrades for you). Do not add any cout statements or modify those that exist. To run the program, you will enter all 14 lab grades or as many lab grades as you have followed by a -1 indicating that there are no more lab scores. Save and compile the program. Copy the le labgrades, and use this le to test your program (redirect your input). Run the program. Notice the -1 indicating that there are no more lab grades to read. Test the program with this le (redirecting the input). This is what your output should be: This program calculates an average, lowest, and highest for up to 14 grades. Enter a list of your grades (14 or fewer): Your average grade is 85.9 Your lowest grade is 67 and your highest grade is 100

76

Laboratory 13
13.1 Objectives

Structures and Yet More Arrays

to understand the dierence between binary and sequential search to be able to program using structures

13.2

Prerequisites

You should understand arrays and for loops in order to do this lab.

13.3

Correspondence to Text

The material in this lab best corresponds to sections 9.5. 9.7, and 9.8 in the text.

13.4

Introduction

We have studied the array, and you have done relatively complex programming with arrays. Arrays allow us to store many dierent objects together, but these objects all need to be the same type. What if we want to have a collection of objects, but the objects are dierent types? For example, if we want to store information about an employee for payroll, we probably would want the employees name, address, SSN, salary, tax withholding, state of residence, and possibly other information. It would be nice if we could store this all as one chunk of data, and that is possible with structures.

13.5
13.5.1

Array Review
Searching an array

Suppose that we have an array of integers in an array called labstudents. The integers represent the student-id. For example, if there were 8 students in labstudents with the ids 34500, 23487, 13546, 55490, 28934, 88224, 74567, and 97345, the array would representing this would be (assuming that we keep the student ids in a sorted order): 13546 0 23487 1 28934 2 34500 3 55490 4 74567 5 88224 6 97345 7

If I wanted to know if a particular person were in the array, I could write a function to look at each element, and return true if the student id is found and false if the student id is not found. But because the array is sorted, I dont even need to look at the whole array. I can do a binary search. A binary search is similar to what you might do if you are looking up a word in the dictionary. You might open the dictionary to the middle, look at the guide words, then go right (towards z) or left (towards a) depending on how your word compares with the guide words. 77

In this array, I can start at the middle part of the array, and look back and forth depending on how the element that Im looking for compares to the element in the array. A function that performed a binary search would return true if the student id that Im looking for matches the middle element (the one that Im looking at now). Otherwise, the function would look at the left part of the array if the student id were less and the right part of the array if the student id were more than the element currently being examined (i.e., the guide number to use the dictionary analogy). The function would continue looking at the middle element of the array portion being examined until we found the element or there was no more of the array to search. If there were no more array to search, the function would return false (i.e., not found). For example, if I looked for student element 30000, I would look at the middle element (index 3). The element stored in index 3 is 34500. 34500 > 30000, so I look to the left in the array. I know that 30000 will not be in slots 4-7 because 30000 < 34500, and the array is sorted. Its like looking a name up in the telephone book or a word up in the dictionary. If the word in the book is George, and youre looking for Adams, you know you need to go to the left (to earlier in the alphabet). If Adams is in the book at all, it will be before George not after George. So, I search the array from 0-2, knowing that if 30000 is in the array, it will be in one of these elements (weve eliminated everything in slots 3-7). In a binary search, the middle element is chosen for comparison. In this case, the middle is index 1 (between 0 and 2) which holds 23487. So, I compare 23487 to 30000. 23487 < 30000, so I look to the right since if 30000 is there, it will be to the right of 23487. Finally, I search the array from 2 to 2 (since weve eliminated 3-7 and 0-1), and do the comparison. 28934 = 30000, and there is no place else to go since Ive looked everywhere else, so I know that Im done looking. Thus, the search consisted of looking at 3 elements: in slot 3 (34500), slot 1 (23487), and slot 2 (28934). This is a lot fewer checks than if wed looked at every element (which would have been 8 checks). Do lab 13 questions Binary Search. Copy binsearch.cpp from the public directory. This program does a binary search using the student ids given above. Copy infile13. Compile and test the program with 30000 and with 97345. The program prints which element is currently being examined so that you can see how the search is getting closer. Try it with several numbers. Modify binsearch.cpp to include a function writearray that prints out all of the elements in the sorted array. Call this function after the searching is complete (i.e., after the if statement in the main function). I have written the cout statements for you in a comment, but I have not written the prototype, call, or denition. What if the le were not sorted? You would have to check every element. Copy binsearch.cpp to search.cpp, and modify the function search so that it looks at every element starting from 0. No other function should be modied. Do not change any of the cout statements. 78

Remember this function does two things: 1) it nds the next guide words, and puts them in begin and end, and 2) it returns 1 if the element is in the array and equal to middle and 0 otherwise (it only looks at one element, not the entire array). For the sequential search program (the one that youre writing now), middle will not be the middle of the array, it will be the rst element that you have not searched (0 the rst time the array is called, then 1, then 2, etc. until either the element is found or the end of the array is reached). Compile and test the le.

13.6

Structures

You have learned one mechanism that allows you to store multiple values in one data structure; now well learn another: the structure. Arrays require that all of the elements be of the same type; structures allow you to store dierent types in the same data structure. For example, if your school wanted to keep track of you, they might create a C++ data structure called student. The data structure would have your initials (they might have your name, but thats a little more complicated), your SSN, your GPA, and the total number of credits that you have successfully completed. That structure might look like: struct student { char first_initial; char last_initial; int SSN; float GPA; float credits; }; Unlike the way weve been declaring arrays, this structure declaration is not a variable; it is a type. It occupies no space; we cant access any part of student without declaring a variable of that type. Lets do that: student labstudent; Now we have a variable, labstudent, that has the structure given above. It has a rst initial, a last initial, a SSN, a GPA, and a credits part. Each of these parts is called a member of the structure. In order to access the GPA for labstudent, you would write: labstudent.GPA The other elds would be accessed in a similar way. Structures can be used in pretty much exactly the same way as arrays, but there are some dierences. An entire structure can be passed as an argument, but unlike arrays, structures can be passed either by value or by reference, so you must specify an & when you are passing a structure by reference (remember that arrays are passed by reference only). Like array elements,

79

individual structure members may also be passed by value or reference. Structures may not be printed using cin and cout statements; individual members must be accessed. Answer questions Structures in lab 13. 13.6.1 Hierarchical Structures

You can use any type inside of a structure including other structures and arrays. For example, the type student might be: struct name { char firstname[8]; char lastname[8]; }; struct SSNform // I have made SSN chars because they are treated that way; // they are not treated as numbers { char first3[3]; char second2[2]; char third4[4]; }; struct student { name studentname; SSNform SSN; float GPA; float credits; }; I have changed the name to be an array of characters to make the example more realistic. The variable declaration, student labstudent, is the same as before. To assign 1 to the rst character (number) of labstudents SSN, you would write: labstudent.SSN.first3[0] = 1; All of the types within a structure must have already been declared which is why SSNform and name were declared before student. In general, to access a hierarchical structure (so called because it has other structures inside it), you write: varname.outermostname.innername (though structures can be more deeply embedded than this). You can see how this would be useful for a student structure because you could have structures about other courses, about bills, etc. that are used in the student structure as well as other structures. 80

13.7

Synthesis

Write a program that reads in information for and writes out information for a variable of type student. Copy my program student.cpp. Write the functions that are not written: readSSN, readGPA, writeSSN, and writeGPA. Also, write the function calls for these functions. The function headers have been written. Save, compile, and test the program.

81

Laboratory 14
14.1 Objectives

Classes

to be able to recognize classes to be able to access member functions to understand the terms involved in using classes

14.2

Prerequisites

You should understand structures and arrays in order to do this lab.

14.3

Correspondence to Text

The material in this lab best corresponds to sections 10.1-10.3 in the text. The class described in this lab, rational, is similar to the class fraction described in section 10.5.

14.4

Introduction

The class is the basis for viewing C++ as an object-oriented language. A class can be almost identical to a structure, but because classes and structures are usually used in dierent situations, in practice, classes and structures look quite dierent.

14.5

Structures Reviewed

A rational number is the ratio of two integers, n and d, where d = 0. We can multiply, divide, and add rational numbers. Lets dene a structure for a rational number: struct rational { int numerator; int denominator; };

14.6

An Introduction to Classes

The above description of a rational number contains only its form; rational numbers can also have many operations associated with them, also: for example, reading, writing, and multiplying rational numbers. A class that described a rational number could contain not only the details about how the number would be represented in memory, but also C++ code that performed the operations on the data. In that case, a rational number class could contain the above structure,

82

as well as the denitions to read, write, reduce, and multiply (and anything else that we wanted to do) rational numbers. But what if, instead of the above structure, a rational number were dened as an array of integers from 0 to 1: int rational[2] In this case rational[0] would be the numerator; rational[1] would be the denominator. In order to multiply two rational numbers, x and y, and store the result in z, the C++ code for the rst would be dierent than the C++ code for the second even though the code would be doing the same thing its just that rational numbers are implemented dierently in the two ways (i.e., the struct and the array). It would be nice if the C++ code for multiplying two rational numbers looked the same no matter what the implementation was. For example, wed like to say: z.mult(x,y); and have the answer stored in z (in fact, it would be even nicer if we could write: z = x * y, and you can do that, but we wont cover it in this lab course). Classes allow us to do that. We dont have to know what the implementation of the class looks like. In fact, you have written programs without knowing how a particular class was implemented; you just used the class just as you use ints and oats without knowing how they are implemented. I have written the rational number class in both the array and the struct implementation. The rest of the lab is about the class rational. Copy the following: test.cpp (this is the main program) ratstruct.h (this is the header le with the structure) ratstruct.cpp (this is the le with the functions for the structure) ratarray.h (this is the header le with the array) ratarray.cpp (this is the le with the functions for the array) Print the les test.cpp, ratstruct.h, and ratstruct.cpp The class rational is dened in the two les ratstruct.h and ratstruct.cpp. Classes have two parts to them: the denition section and the implementation section. The denition section is usually in a .h le, and the implementation section in a .cpp le.

14.7

Denition section

Look at ratstruct.h. This le contains the class denition. 14.7.1 The ifndef compiler directive

It begins with the compiler directives #ifndef RAT H and #define RAT H All of your header les should contain a line similar to this. This line says if I havent already dened all the stu below, 83

then read the stu below for the denition. If I have dened this someplace else, then skip the whole thing (down to the compiler directive #endif, the last line of the le). It doesnt really matter what you call the denition (I called it RAT H, but I could have called it gertrude). The reason that you want this in all of your header les is that you might compile two les together, both of which include this le. You only want the header information there once. You have probably done this, and not even realized it. When you compiled two programs together in the past, it is likely that both of them had an include statement for iostream. Because the header le of iostream begins with an ifndef compiler directive, only one copy of iostream was put in your program. 14.7.2 The class

After the compiler directives the word class begins the class denition. The name of the class is rational. The form of a class denition is: class <nameofclass> { }; The ending curly brace and semi-colon are easy to forget. There are two basic parts to a class denition: the public and the private part. When designing classes, everything should be private except what you want the rest of the world to be able to access (mostly functions). In this case, I allow the rest of the world to initialize, read, write, get the numerator of, get the denominator of, multiply, and reduce rational numbers. In ratstruct.h, there is a comment about what each of these functions does. These functions are declared after the word public: (remember the colon!). We might also do other arithmetic operations and perhaps comparison functions, but I did not write those (they wouldnt be hard to do, and you might want to practice doing that; I just chose a few sample functions). The functions that can be performed on a class are referred to as member functions; member functions can be in the public or the private section. The declaration of these functions looks just like the prototypes that you are used to writing; there is no dierence. Any arguments are in parentheses with the type associated, and any return types are given before the function name. The only exception to this is the function rational which species no return type. This is called a constructor. Ill talk more about this later. After the public: section, there is a private: section. In the private: section are items that only members of the class can access; none of the information in this section is accessible to anyone outside. For example, a structure rat has been declared. This structure is accessible only within the class; if you tried to use it in a program that included this header le, you would get a syntax error. Inside the private section is a variable declaration ratnumber. Variable declarations and data types are referred to as storage attributes or data members. There is also a function declared in the private section of ratstruct.h: findGCD. This function is not declared as public because it is not part of the interface of rational numbers. 84

Answer questions About Class Definitions in lab 14

14.8

Implementation section

ratstruct.cpp contains the implementation section of the class rational. It begins by including the header function. This is typical of the way that classes and denition/implementation les interact. Your classes should contain this line as well. The rest of the le is the implementation of the member functions. With minor exceptions, these look exactly like the functions that you have been writing. One dierence is that every term that is dened within the class needs to have a scope resolution operator, that is a ::. Look at the function readrational. The term void has no scope resolution operator because void is dened outside the class. However, readrational is dened within the class, so it has a rational:: before it. Whenever a term is dened within the class, you will need a scope resolution operator before it. Now, look at the function named rational, the same name as the class. The function rational is called a constructor. Constructors always have the same name as the class name. A constructor is a special function that is executed each time a variable of type rational is declared. A variable of a particular class is called an instance of that class. Well see more about constructors and objects when we look at the program test.cpp. The attribute ratnumber is used in each of the member functions. This storage location holds the rational number being manipulated. For example, in rational (the constructor function), ratnumber is initialized to have a numerator of 0 and a denominator of 1. The constructor function is a special function whose name is always the name of the class itself (thus, for the class rational, the name of the constructor is also rational). Look at function mult. How does one multiply rational numbers, u/v and w/z? n d u * w ----v * z

Thus, the function multiples x by y by multiplying their numerators, and storing them in the resulting numerator, and by multiplying their denominators, and storing them in the resulting denominator. Then the result is reduced. The call to getnumtr and getdenom is done just as weve done other calls to classes (do you remember cin.get?): the name of the object, a dot, and the name of the member function (followed by any arguments in parentheses). For example: x.getnumtr() The call to reduce, however, does not use this form. Thats because the storage item being manipulated in this call to reduce is ratnumber, the variable attribute of the class. The call to reduce will reduce the numerator and denominator in ratnumber to its lowest terms. Answer questions About the Implementation File

85

14.9

Compiling with Classes

There is nothing dierent about compiling programs that use classes than programs that do not use classes. But there are usually certain characteristics associated with compiling when classes are involved: 1. There is usually a header le involved. The header le is included in both the implementation and in the program that is using the class. 2. The implementation portion is usually pre-compiled into an object le. You have created object les earlier this semester. Copy ratstruct.h to rat.h For all of our tests, the header le will be called rat.h, so well copy the dierent header les to that name (that way we dont have to change the include statements for the dierent les). Compile the implementation le. Ask your instructor how to do that (it cannot be compiled into an executable because there is no main function). I will call the le ratstruct.o. For classes that you just use (as opposed to classes that you write), this step will usually already be done. Compile the program that uses the rational class with the object le that you just created. Because you will usually be changing your program only and not the class, you will usually compile your program this way. Ask your instructor how to do that. Now lets test the programs that weve been looking at. Compile ratstruct.cpp and test.cpp as above if you havent already. Test the structure implementation of rational numbers by running the just compiled program with the numbers 2/3 and 1/12 (you will enter these numbers all separately, as: 2 then 3 then 1 then 12). Why does the message Structure Implementation appear three times? Where does that come from? 14.9.1 Adding Rational Numbers

Copy ratstruct.cpp to add.cpp; copy ratstruct.h to add.h; and test.cpp to testadd.cpp. Modify the add.cpp and add.h to include a denition and implementation of a function that adds two numbers together. To add 2 rational numbers, u/v and w/z, use the following formula: n d (u*z) + (v*w) -----------v * z 86

Modify testadd.cpp to call your add function where the mult function is called in test.cpp. Change the * character to a + character in the output, but do not change any other output. Copy add.h to rat.h. You will overlay the old rat.h without add.h (that should be ok because you have another version in ratstruct.h). Compile the rational number class that has the function add in it (add.cpp). Compile your new program. Compile testadd.cpp. Test testadd.cpp with 2/3 and 1/12. 14.9.2 Using a dierent implementation

Now, well use a dierent implementation of a rational numbers to do similar operations. First, lets start with the class that just does multiplication and no addition. Lets see how the program runs dierently if we use the array implementation instead of the structure implementation of rational numbers. Examine ratarray.h. Whats the dierence between this le and ratstruct.h? Copy the le rat.h back to add.h to save any changes that youve done to this le. Copy the le ratarray.h to rat.h (this will overlay your le that contains the function add in it). Compile the implementation le (ratarray.cpp) (assume that the object le is named ratarray.o). Compile test.cpp with and ratarray.o together. Test the program with the same input. You should get the same answer. In fact, everything should be the same, except that in the constructor, I have put the statement Array implementation instead of Structure implementation so that you could tell the dierence! The program that uses the class rational is not aware that the implementation is completely dierent.

14.10

Using classes

Youve run all of the programs, but we havent really even looked at how to use classes. Look at program test.cpp. The declaration of x, y and z as rational looks like any other declaration of a type. x, y, z are instances of the class rational. They can also be referred to as objects of that class. When these three objects are declared, the constructor is invoked, and the values are initialized for each of the objects. In addition, the cout statement in the constructor is printed

87

each time (remember that the constructor is the function rational in the ratstruct.cpp and ratarray.cpp le). The program, test.cpp is referred to as a client, and the class rational as the server because the class serves its client with the information to perform its task. Look at the calls to the member functions. For example, x.readrational () will read a numerator and denominator into the variable ratnumber. Of course, the client does not know thats where the information is being stored; only the server needs to know that information. The syntax of the call is objectname dot member-function-name (and associated arguments); for example, x.readrational (). This is the syntax of a call to a member function of a class. Do questions The Client in lab 14.

14.11

Synthesis

Write a new way to implement the rational number class. Use two integers as the rational number representation instead of an array or structure. The header le has been written for you. Copy le ratints.h from the public directory. Name it rat.h Remember that this will overlay the le that already exists. Make sure that you do not need this le. ratints.h is the header le that you will use. Copy le ratstruct.cpp to ratints.cpp, and modify this le to use two integers instead of a structure. Compile ratints.cpp into an object le. Compile that object le with your client program test.cpp. Test the le with 2/3 and 1/12. Youre done with the lab portion of this class. Congratulations!

88