Site hosted by Angelfire.com: Build your free website today!


HOME | DA CLICK | PICTURES | INFO | BIOGRAPHY | WEEKENDS | LINKS | FORUM

#include void Skip(int N); void main() { clrscr(); cout << "WE WILL SKIP ONE LINE NOW" << endl; Skip(1); cout << "WE WILL SKIP TWO LINES NOW" << endl; Skip(2); cout << "WE WILL SKIP FOUR LINES NOW" << endl; Skip(4); cout << "SKIPPING LINES IS SUCH FUN" << endl; getch(); } void Skip(int N) { int K; for (K = 1; K <= N; K++) cout << endl; } PROG1301.CPP OUTPUT WE WILL SKIP ONE LINE NOW WE WILL SKIP TWO LINES NOW WE WILL SKIP FOUR LINES NOW SKIPPING LINES IS SUCH FUN Program PROG1301.CPP does all kinds of skipping, and what is better, it does the skipping with only one type of Skip function. Even in this one simple example, we have simplified the required programming. 13.4 Value Parameter Functions You may be uncomfortable with the label value parameter. This is especially true since you need to wait for the next chapter before we discuss other types of parameters. Strictly teaching speaking, the label value does not need to be mentioned here. I could have waited until the next chapter. The motivation for the label is quite simple. This book is meant to be a tutorial for learning computer science. But at the same time it is meant to be a reference that you can use to look up information. From that point of view it helps if the table of contents specifically states value parameter functions. And that is what this section will do. A bunch of parameter functions will be shown to help you understand the general syntax and to see a variety of examples. Beep Function Program PROG1302.CPP is quite similar to the previous program. In this case a function is defined that has the ability to beep a specified number of times. The ASCII value (7) generates a short beep from the computer. The process is the same as the previous program example. Some integer value is passed to a function, and then the value is used in a loop. The previous program repeated a number of carriage returns. This program repeats a number of beep. Logically speaking, there is no difference between the two program. // PROG1302.CPP // This program uses a Beep function that beeps a // specified number of times. #include #include void Beep(int N); void main() { clrscr(); cout << "WE WILL BEEP ONCE" << endl; Beep(1); getch(); cout << "WE WILL BEEP TWICE" << endl; Beep(2); getch(); cout << "WE WILL BEEP FOUR TIMES" << endl; Beep(4); getch(); cout << "YOUR TEACHER DOES NOT LIKE THIS PROGRAM" << endl; getch(); } void Beep(int N) { int K; for (K = 1; K <= N; K++) cout << (char) 7; } PROG1302.CPP OUTPUT WE WILL BEEP ONCE WE WILL BEEP TWICE WE WILL BEEP FOUR TIMES YOUR TEACHER DOES NOT LIKE THIS PROGRAM Center Function Output format is a big deal. People buy products because they look nice and people pay attention to displays that are attractive. Frequently, program output needs to be centered. Centering is not a trivial task. Each string needs to be counted, then you need to do some math and decide where to position the string. This is precisely where functions, and especially parameter function shine. You send a string to the clever Center function, and the function does all the computation work for you automatically. // PROG1303.CPP // This program uses a function that centers output strings. #include #include #include #include "APSTRING.H" void Center(apstring S); void main() { clrscr(); Center("PARAMETERS"); Center("IS WHAT MAKES"); Center("FUNCTIONS FUNCTION"); getch(); } void Center(apstring S) { int Len,Tab; Len = S.length(); // apstring string-length function Tab = (80 - Len) / 2; cout << setw(Tab) << "" << S << endl; } PROG1303.CPP OUTPUT PARAMETERS IS WHAT MAKES FUNCTIONS FUNCTION The Center function uses a feature that has not been used before. It is a special function that determines the length of a string. In this case, it is the length of string S. The syntax is S.length(); This is the same syntax that was used with the introduction of object oriented programming in Chapter XI. Since length is a member function of the apstring class, any object of the class must be accessed with the dot.function notation. After the length of the string parameter (S) is determined, the value (Len) is subtracted from 80. This leaves the number of spaces not used on one screen line. This leftover amount is then divided by two to compute the number of spaces that should be displayed on either side of the string. Finally, function setw is used to move an empty string over to the location the string needs to start. Now the beauty of this type of function is that it is great if you understand how the function works, but you can use Center regardless of your understanding level. Stuff the function in your utility library and use it. It is better if you understand the logic, but some functions are quite complex and understanding a function and using a function are not always the same. Rectangle Function with Multiple Parameters The next function example is not in the practical category of a center function. The intention of the Rectangle function is to show a function with multiple parameters. You have used a Rectangle function before. The earlier Rectangle function used some fancy graphics that will not be explained here. This function example displays a text-style rectangle. Not practical yes, but it will help to explain some important parameter concepts. Take a look at the program example and see if you note any important differences from the previous examples. // PROG1304.CPP // This program displays a text-style rectangle. #include #include void TextRectangle(int X1, int Y1, int X2, int Y2); void main() { clrscr(); TextRectangle(20,5,40,10); getch(); clrscr(); TextRectangle(10,5,70,20); getch(); } void TextRectangle(int X1, int Y1, int X2, int Y2) { int X,Y; for (Y = Y1; Y <= Y2; Y++) for (X = X1; X <= X2; X++) { gotoxy(X,Y); cout << '#'; } } Do you see the difference? Look at the function heading and the parameters. There are four parameters, and that is not so odd. Displaying a rectangle requires four pieces of information. The unusual feature is that every parameter requires a data type. Four similar integer variables anywhere in a program are usually defined like: int X1, Y1, X2, Y2; Four integer value parameters in a function heading are defined like: int X1, int Y1, int X2, int Y2; PROG1304.CPP OUTPUT #################### #################### #################### #################### #################### #################### #################### Function TextRectangle is called twice. After the first call the screen is cleared when the program user presses the key. The continued output caused by the second function call to TextRectangle is shown on the next page. PROG1304.CPP CONTINUED OUTPUT ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### ########################################################### Rectangle Function with Mixed Parameters You just saw multiple parameters listed in a function heading. Each one of these parameters was an integer (int) data type. Now you need to see what happens when multiple parameters are not the same data type. We can add one more twist, and one more parameter, to the Rectangle function with a character (char) parameter. The extra parameter can be used to indicate what type of character will be used to display the rectangle. Do not look real hard for something spectacular and different. The extra parameter is tagged on to the end with a comma separator. It really does not behave any different from any other parameter. It just happens to be preceded by a different data type. // PROG1305.CPP // This program displays a text-style rectangle // with mixed parameter types. #include #include void TextRectangle(int X1, int Y1, int X2, int Y2, char C); void main() { clrscr(); TextRectangle(20,5,40,10,'$'); getch(); TextRectangle(20,5,40,10,'&'); getch(); } void TextRectangle(int X1, int Y1, int X2, int Y2, char C) { int X,Y; for (Y = Y1; Y <= Y2; Y++) for (X = X1; X <= X2; X++) { gotoxy(X,Y); cout << C; } } PROG1305.CPP OUTPUT $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$ PROG1305.CPP CONTINUED OUTPUT AFTER CLEARING THE SCREEN &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& The mixed parameters show another difference between defining regular variables and defining variables that are parameters. The parameter list can continue with a separation of a comma. That is not a possibility for regular variable definitions. Consider the following two types of variable definitions: Four integer variables and one character variable defined anywhere in a program are defined like: int X1, Y1, X2, Y2; char C; Four integer value parameters and one character variable in a function heading are defined like: int X1, int Y1, int X2, int Y2, char C; Title Function Students at Berkner High School love this function. Picky old, annoying, Mr. Schram (me) requires a title heading at the start of each function execution. This heading helps to identify the function being executed and checks the global flow of the program, even if the function produces no output. A typical title heading looks like the one below for a ProcessData function. ------------------------------------------------------------------------------ ========================== PROCESSING DATA =============================== ------------------------------------------------------------------------------ This type of heading certainly is somewhat of a pain to create. The name has to be centered, and every title has to be the same length. Students have no great fondness for this particular program requirement. What comes to the rescue now is a highly functional, and specialized function that automatically creates such a title heading. The Title function is also a good example of combining various control structures to create a useful application. This function is not a possibility without control structures. The title function may seem fairly complicated, but keep in mind that several items need to be checked and altered depending on circumstances. A string with an even number of characters will get the exact same number of ( = ) characters on each side of the title string. A string with an odd number of characters requires one extra ( = ) to one side of the string. // PROG1306.CPP // This program uses a function that automatically creates a // title heading for a function or program segment. #include #include #include #include "APSTRING.H" void Title(apstring S); void main() { clrscr(); Title("EXECUTION BEGINS"); cout << endl << endl; Title("PROCESSING DATA"); cout << endl << endl; Title("EXECUTION TERMINATED"); getch(); } void Title(apstring S) { int Length, K, N; Length = S.length(); for (K = 1; K <= 76; K++) cout << "-"; cout << endl; N = (70 - Length) /2; for (K = 1; K <= N; K++) cout << "="; cout << " " << S << " "; if (Length % 2 == 1) N++; for (K = 1; K <= N; K++) cout << "="; cout << endl; for (K = 1; K <= 76; K++) cout << "-"; cout << endl; } PROG1306.CPP OUTPUT ------------------------------------------------------------------------------ ========================== EXECUTION BEGINS ============================== ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ ========================== PROCESSING DATA ============================== ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ ======================== EXECUTION TERMINATED ============================ ------------------------------------------------------------------------------ Students who are required to use a function title heading will rapidly add the Title function to their utility library of useful goodies. Please keep in mind the point of this chapter. The purpose is to learn how to use and define functions with parameters. This is not a chapter on useful and clever tools to make your programming life simpler. Hopefully, you will discover a variety of functions that are useful, and even better, you may create some functions yourself. 13.5 Return Functions Every function that you have created so far has been a void function. The earlier Program Modularity chapter used void functions without parameters and now parameters have been introduced with void functions. There must be some other type of function floating around that is not void. There is and this section will look at these so-called return functions. Understanding the difference between the two functions is very vital. The syntax of defining the two functions is different. The manner in which the two functions are called is different. The purpose of the two functions is also different. It does not take a genius to figure out that there are ample opportunities to confuse the syntax and the use of these functions. Void Function and Return Functions There are two types of functions in C++ „h Void Functions „h Return Functions Void functions perform some task like clearing the screen, skipping lines, drawing a circle, etc. Void functions do not return a value. Void function calls are program statements. A void function is called by using the function name, like: Rectangle(X1,Y1,X2,Y2,Color,CLOSED); Return functions return a value. Traditionally, return functions are for some type of numerical processing that requires the use of a value. Mathematical functions are excellent examples of return functions. A return function is called by using the function name in a program statement that uses the value of the return function like: cout << sqrt(625) << endl; X = abs(Y); Q = pow(X,4); The name return function is very appropriate. This type of function returns a value. In the mathematical sense of functions, the return function makes more sense. We tend to think of mathematical functions as having - or returning - values. With this idea in mind the first return function example will be mathematical and it will be very practical. Frankly, C++ did something weird with math functions. At least, I think it is weird that there is a floor function and there is a ceil function, but there is not a round function. We now have a candidate for a user-defined function. Round Function Check out program PROG1307.CPP and look closely for the difference between the syntax of the earlier void functions and the return function that is presented here. What is the same, and what is different? // PROG1307.CPP // This program uses a Round function. #include #include #include int Round(double RealNr); void main() { double RealNumber; int IntNumber; clrscr(); cout << "Enter a number ===>> "; cin >> RealNumber; IntNumber = Round(RealNumber); cout << RealNumber << " Rounded off becomes " << IntNumber << endl; getch(); } int Round(double RealNr) { RealNr = RealNr + 0.5; return floor(RealNr); } PROG1307.CPP OUTPUT #1 Enter a number ===>> 3.4999 3.4999 Rounded off becomes 3 PROG1307.CPP OUTPUT #2 Enter a number ===>> 3.500001 3.500001 Rounded off becomes 4 We will examine the difference between the two types of functions by showing the Skip function and the Round function side by side for ease of comparison. void Skip(int N) { int K; // loop counter for (K = 1; K <= N; K++) cout << endl; } int Round(double RealNr) { RealNr = RealNr + 0.5; return floor(RealNr); } Similarities: Both functions have a function name followed by a parameter list. Information passed to the function with parameters is done in the precise same way. Each function also needs a function body that is enclosed between braces { } . Differences: The void function starts with the reserved word, void to indicate that no value will be returned. The return function starts with the data type of the value that will be returned by the function. The return function has a special return statement at the conclusion of the function body that indicates which value will be returned. The comparisons above were focused strictly on the actual source code of the two functions. The table below shows examples of calling each one of the functions. Examples calling function Skip Examples calling function Round Skip(2); Skip(2); Skip(Count); Number = Round(X); cout << Round(X) << endl; while (Round(RealNbr) < 10) { cout << ¡¨Enter ---> ¡¨; cin >> RealNbr } Similarities: There are two similarities in the function calls. First, each call needs to use the name of the function in the function call. Second, you need to pass required parameter information to the function. Differences: Void functions are called as stand-alone program statements. Only the function name with the parameter list is used. The return function call is done by using the function name as a value in a program statement. NextInteger Function The next return function example also has a practical value. This function returns the next integer of the value parameter. This type of function can be used in a word processor footer or header, which prints the page number. Each function call, increments the page number. // PROG1308.CPP // This program uses a function that returns the next integer. #include #include int NextInt(int Nr); void main() { int Number,NextNumber; clrscr(); cout << "ENTER AN INTEGER ===>> "; cin >> Number; NextNumber = NextInt(Number); cout << endl << endl; cout << "NEXT NUMBER IS " << NextNumber << endl; getch(); } int NextInt(int Nr) { Nr++; return Nr; // or use return ++Nr; } PROG1308.CPP OUTPUT ENTER AN INTEGER ===>> 1001 NEXT NUMBER IS 1002 Convert Function This convert function shows a pattern for a group of function that can be used to convert values between the English measurement system and the Metric system. This particular function changes Kilograms to Pounds. // PROG1309.CPP // This program uses a function that converts Kilograms to Pounds. #include #include double Convert(double Kg); void main() { double Kilos, Pounds; clrscr(); cout << "Enter weight in Kilograms ===>> "; cin >> Kilos; Pounds = Convert(Kilos); cout << endl; cout << Kilos << " Kilograms = " << Pounds << " Pounds" << endl; getch(); } double Convert(double Kg) { return Kg * 2.2; } PROG1309.CPP OUTPUT Enter weight in Kilograms ===>> 1000 1000 Kilograms = 2200 Pounds Boolean Password Function Return functions can return a wide variety of data types, including our newly acquired friend bool. Such a function becomes ideal for checking proper program user input. The next program checks if the correct password is entered. Notice that the data type being returned by the function does not need to be the same as the data type of any parameter passed to the function. // PROG1310.CPP // This program uses a Boolean function to determines if the // entered password is correct. #include #include #include "BOOL.H" #include "APSTRING.H" bool OkPassword(apstring Password); void main() { apstring Password; bool Correct; clrscr(); do { cout << "Enter the password ===>> "; cin >> Password; Correct = OkPassword(Password); if (Correct) cout << "Password checks; you may proceed" << endl; else cout << "Incorrect password; please re-enter" << endl; } while (!Correct); getch(); } bool OkPassword(apstring Password) { return (Password == "SPOCK"); } PROG1310.CPP Enter the password ===>> QWERTY Incorrect password; please re-enter Enter the password ===>> Spock Incorrect password; please re-enter Enter the password ===>> SPOCK Password checks; you may proceed Boolean OkChar Function The next function will not seem very practical at first, but the very next program will demonstrate its usefulness. This function is a Boolean function that determines if an entered character is a lower-case letter. The function works by using a compound statement that checks if the entered character is greater than or equal to lower-case ¡¥a¡¦ and at the same time less than or equal to lower-case ¡¥z¡¦. // PROG1311.CPP // This program uses a Boolean function to determines if the // input is a lower-case letter. #include #include #include "BOOL.H" bool OkChar(char Ltr); void main() { char Letter; clrscr(); cout << "Enter a lower-case letter ===>> "; cin >> Letter; cout << endl; if (OkChar(Letter)) cout << "Lower-case letter entered is " << Letter << endl; else cout << "You did not enter a lower-case letter" << endl; getch(); } bool OkChar(char Ltr) { return ((Ltr >= 'a') && (Ltr <= 'z')); } PROG1311.CPP OUTPUT Enter a lower-case letter ===>> # You did not enter a lower-case letter UpCase Function Program PROG1312.CPP is a neat program. This program changes lower-case letters to upper-case letters. The task sounds pretty simple, but there are some special considerations. You cannot change a pound sign ( # ) into an upper-case letter, and you do not need to change any letter, which is already an upper-case letter. What comes to the rescue is our OkChar function, introduced in the last program. Before function UpCase is called, a call is made to function OkChar to make sure that the entered character is a lower-case letter. Function UpCase is designed to digest only lower-case letters. // PROG1312.CPP // This program demonstrates a function that returns the // upper-case of an entered lower-case letter. // The lower-case function of the previous program is used. #include #include #include "BOOL.H" bool OkChar(char Ltr); char UpCase(char Ltr); void main() { char Letter; clrscr(); cout << "Enter a lower case letter ===>> "; cin >> Letter; cout << endl; if (OkChar(Letter)) cout << "Upper-case letter is " << UpCase(Letter) << endl; else cout << "You did not enter a lower case letter" << endl; getch(); } bool OkChar(char Ltr) { return (Ltr >= 'a') && (Ltr <= 'z'); } char UpCase(char Ltr) { return(Ltr - 32); } PROG1312.CPP OUTPUT Enter a lower-case letter ===>> a Upper-case letter is A You may be confused how the statement . . . return (Ltr - 32); . . . managed to convert a lower-case letter to an upper-case letter. Remember that all characters are stored as integers. The stored integer is the ASCII value of the character. Now the upper-case letters have the sequence 65, 66, 67, 68, etc. for the letters A, B, C, D, etc. The lower-case letters have the ASCII sequence 97, 98, 99, 100, etc. for the letters a, b, c, d, etc. Notice that the ASCII value of 65 for A is 32 less than the value 97 for a. This same difference applies for all the letter characters. The result is that subtracting 32 from any lower-case letter will return the corresponding upper-case letter. GCF Function Many years ago, Euclid cleverly invented an algorithm for computing the Greatest Common Factor of two integers. The algorithm uses the following steps: 1. Divide Nbr1 by Nbr2 120 / 108 2. Determine the remainder 120 / 108 = 1 + rem 12 3. If the remainder is 0, you are done Remainder is not 0 The GCF is the divisor 4. If the remainder is not zero, Nbr1 becomes 108 Nbr1 becomes Nbr2 and Nbr2 becomes 12 Nbr2 becomes the remainder 5. Goto step 1 Divide Nbr1 by Nbr2 108 / 12 6. Determine the remainder 108 / 12 = 9 + rem 0 7. If the remainder is 0, you are done The GCF is 12 The GCF is the divisor Program PROG1313.CPP uses function ComputeGCF, which implements Euclid¡¦s algorithm. The earlier algorithm repeated several steps. In a program, the steps are placed in a loop until the remainder is zero. // PROG1313.CPP // This program demonstrates a GCF function. // This program also creates a function warning because // the return statement is not the last function statement. #include #include int ComputeGCF(int N1, int N2); void main() { int Number1,Number2; int GCF; clrscr(); cout << "Enter integer 1 ===>> "; cin >> Number1; cout << "Enter integer 2 ===>> "; cin >> Number2; GCF = ComputeGCF(Number1,Number2); cout << endl; cout << "GCF of " << Number1 << " and " << Number2 << " is " << GCF << endl; getch(); } int ComputeGCF(int N1, int N2) { int Rem; do { Rem = N1 % N2; if (Rem == 0) return (N2); else { N1 = N2; N2 = Rem; } } while (Rem != 0); } PROG1313.CPP OUTPUT Enter integer 1 ===>> 120 Enter integer 2 ===>> 108 GCF of 120 and 108 is 12 This program works fine but you may be disturbed by the compiler warning. You really prefer to get a success message, and any type of message that indicates something is wrong is not a good message. The message is a warning, and the warning indicates something is wrong with the return statement. The return statement is just fine, but the compiler gets excited because the return statement is not the last statement in the function. The solution to this problem (if warnings bother you) is to create a local variable in function ComputeGCF. The local variable, GCF, is used inside the loop for the value of the GCF. After the loop is finished, it is this value that is returned by the function. You may also argue that not only did the warning message disappear, but also a second improvement was made in readability. The statement GCF = Nr2; does improve the clarity of the function. Furthermore, the statement return GCF also helps to explain what the purpose of the function is. // PROG1314.CPP // This program demonstrates a GCF function. // This program cures the warning problem with a local variable. #include #include int ComputeGCF(int N1, int N2); void main() { int Number1,Number2; int GCF; clrscr(); cout << "Enter integer 1 ===>> "; cin >> Number1; cout << "Enter integer 2 ===>> "; cin >> Number2; GCF = ComputeGCF(Number1,Number2); cout << endl; cout << "GCF of " << Number1 << " and " << Number2 << " is " << GCF << endl; getch(); } int ComputeGCF(int N1, int N2) { int Rem,GCF; do { Rem = N1 % N2; if (Rem == 0) GCF = N2; else { N1 = N2; N2 = Rem; } } while (Rem != 0); return GCF; } PROG1314.CPP OUTPUT Enter integer 1 ===>> 108 Enter integer 2 ===>> 120 GCF of 108 and 120 is 12 13.6 Scope No this is not a commercial for a mouthwash. We are finishing this chapter by looking at scope. The concept of scope is not particularly significant with small programs that do not use functions. On the other hand, programs that use many functions can have peculiar problems because students may not understand the scope issue. So what exactly is Scope? Scope Definition Scope is the part of a C++ program where a variable is defined. Are you now totally clear on the meaning of scope? Probably not. The definition is short and straight-forward because the concept of scope is straight-forward. However, that does not translate into clarity in your brain. Let¡¦s go to a law-enforcement analogy. The Dallas city police can arrest criminals and handle criminal cases in the city of Dallas. They do not have jurisdiction outside Dallas. The Dallas county sheriff can cover the entire county of Dallas and the State Police can handle criminal cases anywhere in the state of Texas. Finally, the FBI can handle cases in the entire United States. Each one of the mentioned agencies is a law enforcement agency. The difference between the agencies is jurisdiction. Jurisdiction is the part of the country where an agency has the legal right to enforce the law. A Dallas policeman does not take a vacation in New York city and suddenly start arresting drug dealers. The Dallas officer does not have jurisdiction in New York city. This is fine with law enforcement but you are dealing in the world of C++. I believe that the concept of scope can be explained by looking at two very strange programs. These programs have a variable defined and initialized all over the place. There is plenty more to say about this issue. Right now check out the first program and look closely at the output execution. // PROG1315.CPP // This program demonstrates scope with different variables. #include #include int X1 = 100; void Function1(); void Function2(); void main() { clrscr(); cout << "Global X1 = " << X1 << endl; int X2 = 200; cout << endl; cout << "Main Function X2 = " << X2 << endl; Function1(); cout << endl; cout << "Main Function X2 = " << X2 << endl; Function2(); getch(); } void Function1() { int X3 = 300; cout << endl; cout << "Function1 X3 = " << X3 << endl; if (X3 == 300) { int X4 = 400; cout << endl; cout << "Very Local X4 = " << X4 << endl; } cout << endl; cout << "Function1 X3 = " << X3 << endl; } void Function2() { cout << endl; cout << "Global X1 = " << X1 << endl; } PROG1315.CPP OUTPUT Global X1 = 100 Main Function X2 = 200 Function1 X3 = 300 Very Local X4 = 400 Function1 X3 = 300 Main Function X2 = 200 Global X1 = 100 In all likelihood you do not have a clue what program PROG1315.CPP is supposed to show you. If scope is the issue at hand, then the program - just demonstrated - fails to explain or motivate any new concepts. You did see four variables, X1, X2, X3 and X4. Each one of the variables was defined for a different scope of the program. X1 has global scope and can be used anywhere in the program. X2 has a scope that is limited to the main function. X3 has a scope that is limited to Function1, and X4 is a peculiar - very local - variable that is limited to the body of the if statement. So what does that show about scope? Everything . . . if you understand scope and probably nothing if you have not heard of scope before. Hang on, clarity is around the corner because we had to set the stage properly for the next program. Program PROG1316.CPP is very similar to the previous program. And the output is completely identical. The interesting feature is that this identical output is accomplished with the same variable identifier everywhere. Yet the output proves that the same identifier represents the same four different variables of the earlier program. How is this possible? Scope is the answer. The program is designed to demonstrate scope and you may need to go back and forth between the explanation and the program. Check everything carefully. This program shows not only scope, but also the concept that a local variable takes precedence over a global -- or less local -- variable with the same name. Please do not use variable names as the program example demonstrates. This is only for the sake of explaining a concept. // PROG1316.CPP // This program demonstrates scope with the same identifier. #include #include int X = 100; // global scope X void Function1(); void Function2(); void main() { clrscr(); cout << "Global X = " << X << endl; int X = 200; // main function scope X cout << endl; cout << "Main Function X = " << X << endl; Function1(); cout << endl; cout << "Main Function X = " << X << endl; Function2(); getch(); } void Function1() { int X = 300; // function1 scope X cout << endl; cout << "Function1 X = " << X << endl; if (X == 300) { int X = 400; // very local scope X cout << endl; cout << "Very Local X = " << X << endl; } cout << endl; cout << "Function1 X = " << X << endl; } void Function2() { cout << endl; cout << "Global X = " << X << endl; } PROG1316.CPP Global X = 100 Main Function X = 200 Function1 X = 300 Very Local X = 400 Function1 X = 300 Main Function X = 200 Global X = 100 The program starts with the definition of the global X. In the main function the value of 100 is displayed. X is defined locally inside the main function with value 200. This local definition supersedes the global X. Keep in mind that it does not alter the global X. It just means that inside the main function X = 200 takes over. Function1 also has a local X initialized to 300. Then something unusual happens. X is defined one more time inside the body of the if statement. This looks like a duplicate identifier, but it is not. The scope of X = 400 is different. You now have seen X defined 4 times and initialized four times with a different value. Now comes the interesting part. In backward order we return to the previous X values, and what do you know? All the values are present as they were. The redefining Xs have not altered anything. Every X has politely stayed out off the scope of another X. Frequently multiple X identifiers are in the same scope, but this is OK. For instance, the very first X = 100 is global and has global scope. However, in the main function X = 200 is local and precedes the first X value. Problems with Scope All this scope business makes for good table conversation. You may assume that you do not need to be concerned with this nonsense, because there is no way that you will write a program that has identical identifiers floating all over the place. With different variable names, everything falls in place and confusion is avoided. True, you do avoid confusion, and true again you definitely should not write programs with identical identifiers everywhere. But, false, you do need to understand scope. Consider the next short program example. Program PROG1317.CPP is a short innocent program designed to enter five numbers from the keyboard. After the loop is finished, the final number is displayed. // PROG1317.CPP // This program demonstrates problems with scope. // The program does not compile because of the scope // limitation of the variable. #include #include void main() { clrscr(); int X; for (X = 1; X <= 5; X++) { int Y; cout << "Enter a number ===>> "; cin >> Y; } cout << "The last value entered was " << Y << endl; getch(); } PROG1317.CPP OUTPUT There is no output. This program does not compile. It has an unknown identifier error. The X variable is defined at the beginning of the main function. X is certainly defined and works properly throughout the main function. Inside the body of the for loop structure, Y is defined. The result is that Y now has the limited scope, which exists between the braces of the loop body. If we keep Y inside the loop, everything will work just fine. However, the Y output statement after the loop causes problems for the compiler. If you comment out the last cout statement, you will find that the program compiles and runs without problem. The real solution to the scope problem of the previous program is knowing where to define variables. Program PROG1318.CPP is almost identical to the previous program. One small - but significant - change is made. The Y variable is defined early in the main function, and not inside the loop body. // PROG1318.CPP // This program demonstrates how to solve the scope problem // of the previous program by defining the Y variable early // in the program. #include #include void main() { clrscr(); int X,Y; for (X = 1; X <= 5; X++) { cout << "Enter a number ===>> "; cin >> Y; } cout << endl; cout << "The last value entered was " << Y << endl; getch(); } PROG1318.CPP OUTPUT Enter a number ===>> 1 Enter a number ===>> 2 Enter a number ===>> 3 Enter a number ===>> 4 Enter a number ===>> 5 The last value entered was 5 Scope Precedence Rule When two same-named identifiers are both in scope, the local identifier takes precedence. int X = 25; void Hello() { int X = 100; cout << "X = " << X << endl; } void main() { Hello(); cout << "X = " << X << endl; } 13.7 Preconditions and Postconditions You have used functions for quite some time. True, functions with parameters may be new, but the concept of breaking down a program into separate modules is something you learned many chapters ago. At this stage in C++ you know how to use the C++ function to write a program module. This means that writing functions is a very important part of writing programs. If functions are the building blocks of a program then these blocks must all work correctly. The correctness of a program relies on the correctness of the individual functions used by a program. One small function, incorrectly written, used by a very large program can easily crash the program. Program reliability is at the very top of the program requirement list. Function preconditions and postconditions are a major factor in insuring that functions work correctly. Briefly explained a precondition states what is true before a function executes, and a postcondition states what is true when a function has finished executing. Precondition and Postcondition Definition A precondition is a statement that explains what is true before a function executes. A postcondition is a statement that explains what is true after a function finished executing - if the precondition was true when the function started executing. Note the very important if part of the postcondition definition. A postcondition must assume that the precondition requirement is satisfied. In other words, if the precondition is not met than all bets are off. Probably this is all very murky sounding so let's consider a couple of program examples. Program PROG1319.CPP uses function DisplaySquareRoot, which is a function that computes the square root of the N parameter and then displays the result. This program is written without any concern about preconditions. // PROG1319.CPP // This program uses a function without preconditions. // The program will result in a runtime error for // negative numbers. #include #include #include void DisplaySquareRoot(double N) { cout << endl << endl; double Result = sqrt(N); cout << "The square root of " << N << " is " << Result << endl; } void main() { clrscr(); double Number; cout << "Enter a number ===>> "; cin >> Number; DisplaySquareRoot(Number); getch(); } PROG1319.CPP OUTPUT #1 Enter a number ===>> 1024 The square root of 1024 is 32 PROG1319.CPP OUTPUT #2 Enter a number ===>> -100 Runtime error. Output will fluctuate with compilers The first output of PROG1319.CPP does not show any difficulties. A number is entered and the square root of the number is displayed. The second output is a different story. This time -100 is entered and the square root function cannot digest the negative input. Some type of runtime error will occur. Function DisplaySquareRoots does its job, but there needs to be a better way. In the next program example you will note that a comment has been added to function DisplaySquareRoots. The comment is a so called precondition, and the condition states that parameter N must be greater than 0. Now do not get confused, stating a precondition does not guarantee that the precondition will be true. The whole point is that the precondition explains what must be true before the function executes. Frequently, void functions may only indicate preconditions for the parameters passed to the function. The first few examples will only use preconditions. Later return function examples will also add postconditions. Function DisplaySquareRoots goes another step and provides protection in the event that the precondition is false. If N > 0, everything is fine and the square root of N will be displayed, otherwise an error message is displayed. You are witnessing a common approach to program reliability. First consider what the precondition of a function should be, and then decide how the function should behave when the precondition is not satisfied. // PROG1320.CPP // Function DisplaySquareRoots has a precondition and a // conditional statement to protect against erroneous // parameter values. #include #include #include void DisplaySquareRoot(double N) // precondition: n > 0 { cout << endl << endl; if (N > 0) { double Result = sqrt(N); cout << "The square root of " << N << " is " << Result << endl; } else { cout << "ERROR" << endl; cout << "Incorrect parameter value" << endl; } } void main() { clrscr(); double Number; cout << "Enter a number ===>> "; cin >> Number; DisplaySquareRoot(Number); getch(); } PROG1320.CPP OUTPUT #1 Enter a number ===>> 1024 The square root of 1024 is 32 PROG1320.CPP OUTPUT #2 Enter a number ===>> -100 ERROR Incorrect parameter value You may never have thought must about the reliability of the humble Skip function. The usual Skip function is shown in the next program - comments - with the precondition version. Now the nature of the for loop is such that a negative N value will not cause any problems. The loop will simply not execute at all, and no lines are skipped. You might also argue that it is good to know if some negative parameter is passed to this function. Such a Skip function is shown below complete with error message for erroneous parameter values. // PROG1321.CPP // This program demonstrates how to make function // Skip more reliable. #include #include #include //void Skip(int N) //{ // for (int K = 1; K <= N; K++) // cout << endl; //} void Skip(int N) // precondition: 0 < N < 50 { if (N > 0 && N < 50) { for (int K = 1; K <= N; K++) cout << endl; } else { cout << "ERROR" << endl; cout << "Incorrect parameter value" << endl; } } void main() { clrscr(); int Number; cout << "Enter Skip Number ===>> "; cin >> Number; cout << "BEFORE SKIP CALL" << endl; Skip(Number); cout << "AFTER SKIP CALL" << endl; getch(); } PROG1321.CPP OUTPUT #1 Enter Skip Number ===>> 3 BEFORE SKIP CALL AFTER SKIP CALL PROG1321.CPP OUTPUT #2 Enter Skip Number ===>> -4 BEFORE SKIP CALL ERROR Incorrect parameter value AFTER SKIP CALL It is easiest to explain postconditions with return functions. Return functions return some value, and the postcondition normally indicates what type of value needs to be returned. Program PROG1322.CPP states in the precondition that the parameter, Ltr, must be a lower-case letter. The postcondition requires that the functions returns the upper-case letter of Ltr. At the same time function UpCase checks to make sure that Ltr is a lower-case letter. An error message is displayed when the precondition is not satisfied. // PROG1322.CPP // This program demonstrates a function that returns the // upper-case of an entered lower-case letter. // Protection against erroneous parameter value is provided. #include #include char UpCase(char Ltr) // precondition: 'a' <= Ltr <= 'z' // postcondition: Upcase returns the upper-case of Ltr. { if (Ltr >= 'a' && Ltr <= 'z') { return(Ltr - 32); } else { cout << "ERROR" << endl; cout << "Ltr is not a lower-case letter" << endl; return Ltr; } } void main() { char Letter; clrscr(); cout << "Enter a lower case letter ===>> "; cin >> Letter; cout << Letter << " becomes " << UpCase(Letter) << endl; cout << endl; getch(); } PROG1322.CPP OUTPUT #1 Enter a lower case letter ===>> q q becomes Q PROG1322.CPP OUTPUT #2 Enter a lower case-letter ===>> 8 ERROR Ltr is not a lower-case letter 8 becomes 8 It is not always necessary or desirable to display an error message when the precondition of a function is not satisfied. Often the return value of a function can provide important information about the correctness of the function execution. The next program example repeats the GCF function, shown earlier this chapter, and adds some requirements for precondition and postcondition. This time there is no error message provided. However, if the precondition of the function is not satisfied, the postcondition states that 0 needs to be returned. // PROG1323.CPP // This program demonstrates the GCF function with // preconditions and postconditions. #include #include int ComputeGCF(int N1, int N2) // precondition: N1 > 0, N2 > 0 // postcondition: ComputeGCF returns the GCF of N1 and N2 // if N1 > 0 and N2 > 0, and returns 0 otherwise. { int Rem,GCF; if (N1 > 0 && N2 > 0) { do { Rem = N1 % N2; if (Rem == 0) GCF = N2; else { N1 = N2; N2 = Rem; } } while (Rem != 0); } else GCF = 0; return GCF; } void main() { int Number1,Number2; int GCF; clrscr(); cout << "Enter integer 1 ===>> "; cin >> Number1; cout << "Enter integer 2 ===>> "; cin >> Number2; GCF = ComputeGCF(Number1,Number2); cout << endl; cout << "GCF of " << Number1 << " and " << Number2 << " is " << GCF << endl; getch(); } PROG1323.CPP OUTPUT #1 Enter integer 1 ===>> 120 Enter integer 2 ===>> 108 The GCF of 120 and 108 is 12 PROG1323.CPP OUTPUT #2 Enter integer 1 ===>> -14 Enter integer 2 ===>> -56 The GCF of -14 and -56 is 0 You may or may not find it very practical to return a value of zero. The virtue of that return value is not the issue here. You do have the ability to use that value in some way that makes the program behave appropriately without crashing. It is very common in the computer science world to have functions return a variety of different codes when a function does not work correctly. Different codes can be used for different purposes. 13.8 Worked-Out Exercises The worked-out exercises in this chapter will deviate from the exercises that were presented in earlier chapters. Previous chapters presented complete programs. In this chapter, and future chapters, you will more likely see a single function presented. In each case you will be asked to determine the output of the function, if it is a void function. You may also be asked to figure out the value returned by the function, if it is a return function. You can assume that all necessary libraries have been included for the program statements in the function to compile properly. Variable trace tables will be used, as before, but the focus is strictly on the variables used in each function. APCS Examination Alert The APCS Examination will very rarely present an complete program on either the multiple choice segment or the free response segment of the test. understanding functions and writing functions is the common practice. Exercise 01 void Ex1301(int N) { if (N % 2 == 0) cout << endl << endl; else cout << endl; } Ex1301(10); N N % 2 == 0 Output 10 true 2 Exercise 02 void Ex1302(apstring Name, double HW, double HR) { double GrossPay = HW * HR; double Deductions = GrossPay * 0.15; double NetPay = GrossPay - Deductions; cout << "Pay to the order of " << Name << " $" << NetPay << endl; } Ex1302("Thomas Phillips",20,6.35); Name HW HR GrossPay Deductns NetPay Thomas 20 6.35 127.00 19.05 107.95 Exercise 03 void Ex1303(int P, int Q) { if (P > Q) P -= Q; else P += Q; cout << "P + Q = " << P + Q << endl; } Ex1303(5,10); P Q P > Q P + Q 5 10 false 15 25 Exercise 04 void Ex1304(int N) { int Temp = 1; for (int K = N; K >= 1; K--) Temp *= K; cout << "Temp = " << Temp << endl; } Ex1304(5); N Temp K 5 1 5 5 4 20 3 60 2 120 1 120 Exercise 05 void Ex1305(int P, int Q, int R) { int X = P; if (Q > X) X = Q; if (R > X) X = R; cout << X << endl; } Ex1305(14,25,16); P Q R X 14 25 16 14 25 Exercise 06 int Ex1306(int P, int Q) { while (P < Q) { P++; Q--; } return P + Q; } Ex1306(7,25); P Q return P + Q 7 25 8 24 9 23 10 22 11 21 12 20 13 19 14 18 15 17 16 16 32 Exercise 07 int Ex1307(int P, int Q) { while (P <= Q) { if (Q % P == 2) Q--; else P++; } return P + Q; } Ex1307(5,10); P Q Q % P return P + Q 5 10 0 6 4 7 3 8 2 9 1 9 0 10 19 Exercise 08 int Ex1308(int N) { int Temp = 1; for (int K = 1; K <= N; K+=K) Temp += K; return Temp; } Ex1308(15); N K Temp return Temp 15 1 1 2 2 4 4 8 8 16 16 16 Exercise 09 int Ex1309(int N) { int Temp = 1; for (int K = 1; K <= N; K++) Temp += Temp; return Temp; } Ex1309(6); N K Temp return Temp 6 1 1 2 2 4 3 8 4 16 5 32 6 64 64 Exercise 10 int Ex1310(int N) { int Temp = 100; for (int K = N; K > 0; K--) Temp -= Temp; return Temp; } Ex1310(10); N K Temp return Temp 10 100 10 0 9 0 8 0 7 0 6 0 5 0 4 0 3 0 2 0 1 0 0



Chapter XIV Parameter Functions II Chapter XIV Topics 14.1 Introduction 14.2 Void Functions that Return Values 14.3 One-Way, Two-Way, and Mixed-Way Parameters 14.4 Parameter Vocabulary 14.5 Parameters, a Technical Explanation 14.6 Test Your Parameter Knowledge 14.7 Parameters Without Identifiers 14.8 Inline Functions 14.9 Overloaded Functions 14.10 Parameter Rules 14.11 Worked-Out Exercises 14.1 Introduction Functions must be bothering you by now. Many chapters ago you were introduced to simple functions. Life was good back then. There was minimal headaches about parameters, and modularity was the main concern. Now you are getting bombarded by functions and the end does not seem to be in sight yet. The last chapter introduced user-defined functions that used parameters. You used both void and return functions. The name of the parameters in those functions were called value parameters. A value is passed to a function. The function makes a copy of the passed value, and processes information according to the program statements in the function. Now there is still more function business. In this chapter we will talk about variable or reference parameters. This type of parameter is very different from the earlier value type. It appears that this parameter has the ability to pass information back to the calling program segment. Take all this parameter business seriously. You need a very thorough, solid, understanding of parameters. It will be the foundation of everything that follows in future chapters. Whether you continue computer science in high school or whether you continue in college or the business world, computer science will expect a thorough knowledge of parameters. So what is the story on these variable-whatever-reference parameters? Well, consider this. Right now the way to get information from a function is with a return function. This works very well in many situations, but there is a serious limitation. A return function returns a single value. Frequently, there is a need to return multiple values from a function. How is that accomplished? You guess very well. It is done with these nifty variable/reference parameters, which is the main focus of this chapter. One word of warning before you plunge into this chapter. You need to be on fairly solid ground with void functions and return functions. If you have casually glanced through the previous chapter or if you are unsure, even after serious studying, go back. Adding the information in this chapter on top of existing function-confusion will not be real productive. Study the previous chapter some more, get extra help, but whatever you do, learn the previous material first. 14.2 Void Functions that Return Values Simple void functions can only pass information with global variables. That is a serious nono. Ugly, yukky, side effects can creep into your programs with global variable information passing. You did use that approach for a while for the sake of going through a gentle introduction to program modularity. That stage has now passed and you are entering the proper world of programming development. Void functions with value parameters pass information in one direction. The program segment that calls the function sends information to the function. The void function is on the receiving end of the parameter passing business. We also have return functions that have the ability to return information from the function. These functions receive information - usually with value parameters - digest the information and return some value. We could call these return functions, two-way information functions. There is yet another information passing approach. You can also use void functions that are two-way information functions. The purpose of this section is to investigate how it is possible - and why it is desirable - to use void functions for any two-way information schemes. Take a quick look at two return functions that were shown in the previous chapter. Program PROG1401.CPP and PROG1402.CPP use functions that return the next integer and the upper-case of a lower-case letter. These programs are repeated for comparison reasons. // PROG1401.CPP // This program uses a return function to get the next integer. #include #include int NextInt(int Nr); void main() { clrscr(); int Number; cout << "ENTER AN INTEGER ===>> "; cin >> Number; cout << endl; cout << "NEXT INTEGER IS " << NextInt(Number) << endl; getch(); } int NextInt(int Nr) { Nr++; return Nr; } PROG1401.CPP OUTPUT ENTER AN INTEGER ===>> 19 NEXT INTEGER IS 20 // PROG1402.CPP // This program uses a return function to change a lower-case // letter to an upper-case letter. #include #include char UpCase(char Ltr); void main() { char Letter; clrscr(); cout << "ENTER A LOWER CASE LETTER ===>> "; cin >> Letter; cout << endl; cout << "UPPER CASE LETTER (IF POSSIBLE) IS " << UpCase(Letter); cout << endl; getch(); } char UpCase(char Ltr) { if ((Ltr >= 'a') && (Ltr <= 'z')) Ltr = Ltr - 32; return Ltr; } PROG1402.CPP OUTPUT ENTER A LOWER CASE LETTER ===>> a UPPER CASE LETTER (IF POSSIBLE) IS A Program PROG1403.CPP uses a void function to accomplish the same process as the earlier program did with a return function. Check out the program below and observe any differences compared to the previous return functions. // PROG1403.CPP // This program uses a void function to get the next integer. #include #include void NextInt(int &Nr); void main() { clrscr(); int Number; cout << "ENTER AN INTEGER ===>> "; cin >> Number; cout << endl; NextInt(Number); cout << "NEXT INTEGER IS " << Number << endl; getch(); } void NextInt(int &Nr) { Nr++; } PROG1403.CPP OUTPUT ENTER AN INTEGER ===>> 19 NEXT INTEGER IS 20 Void function NextInt is different from the return function NextInt because of the difference between void and return functions. That is not new. Both the function syntax and the function calling approach is different between void and return functions. These differences can be reviewed in the last chapter. The difference you should have observed is an odd looking ampersand (&) in the function heading of NextInt. void NextInt(int &Nr) The output of the program indicates that it does provide the next integer to the main function. Try the program yourself, you will find that it works correctly and apparently behaves in the same manner as the earlier program with the return function. Now look at program PROG1404.CPP and see if you can detect any type of pattern. This program alters a lower-case letter to an upper-case letter. You can trust the provided output, but better yet, type the program in yourself and test yourself to see that the output matches your expectations. // PROG1404.CPP // This program uses a void function to change a lower-case // letter to an upper-case letter. #include #include void UpCase(char &Ltr); void main() { char Letter; clrscr(); cout << "ENTER A LOWER CASE LETTER ===>> "; cin >> Letter; cout << endl; UpCase(Letter); cout << "UPPER CASE LETTER (IF POSSIBLE) IS " << Letter; cout << endl; getch(); } void UpCase(char &Ltr) { if ((Ltr >= 'a') && (Ltr <= 'z')) Ltr = Ltr - 32; } PROG1404.CPP OUTPUT ENTER A LOWER CASE LETTER ===>> a UPPER CASE LETTER (IF POSSIBLE) IS A Is an pattern noticeable? There should be ... by looking at the function heading. Once again, an odd & is noticed in the function heading next to the parameter. The function headings really look pretty much like regular void function headings that use value parameters, first introduced in the last chapter. Everything is the same, except for the &. void NextInt(int &Nr) void UpCase(char &Ltr) The innocent looking (&) has a tremendous impact on parameters Nr and Ltr. Suddenly Nr and Ltr are no longer value parameters, limited in receiving information in one direction from the corresponding parameters in the function calls. Nr and Ltr are now variable parameters. It is now possible to pass information in both directions. The magic (&) not only allows information to be passed to the function. Some process can then occur in the function like computing the next integer, and yes information can then be passed from the function back to the calling segment. You are entirely unimpressed. Right now it seems that void functions with the assistance of a weirdly placed (&) manage to accomplish the same task as the return function already did in the last chapter. Furthermore, you were much more impressed with the return function¡¦s approach. Each return function performed its required process and at the conclusion of the function, the clever return statement passed the appropriate value back to the calling program segment. Now computer scientists as a rule are a logical bunch of people. The inclusion and creation of some feature is normally for the purpose of accomplishing something that cannot be done some other way. At least the required task cannot be done as efficiently without the newly included feature. You see, return functions have one very important limitation: return functions only return one value. The examples that were used returned the next integer and the upper-case letter. Consider all the common math functions, like square root, absolute value and rounding off. All such functions only return a single value. In each one of those situations you should use a return function. But there are other situations and a good example is function Swap. This functions requires that two parameters exchange their values. In other words, two values need to be returned to the calling program segment. In this case the return function politely bows its head and allows some other function to do the job. Program PROG1405.CPP demonstrates how to exchange values with a Swap void function that uses two variable parameters. // PROG1405.CPP // This program returns two altered parameters. // It demonstrates a swap function. #include #include void Display(int A, int B); void Swap(int &A, int &B); void main() { int N1 = 5, N2 = 7; clrscr(); Display(N1,N2); Swap(N1,N2); Display(N1,N2); getch(); } void Display(int A, int B) { cout << endl << endl; cout << "A = " << A << endl; cout << "B = " << B << endl; } void Swap(int &A, int &B) { int T; T = A; A = B; B = T; } PROG1405.CPP OUTPUT A = 5 B = 7 A = 7 B = 5 The Swap function does not show any special magic. The same & business shown with NextInt and UpCase is now done with Swap. The & is used twice because we are using two variable parameters, but logically the concept is the same. This type of function is precisely why void functions with two-way parameters exist. Try swapping with a return function. You will need some serious imagination to accomplish that trick. We can even get fancier and use three variable parameters. How about function Sort that uses three integer parameters and returns the three parameters neatly sorted? That would be pretty impressive. A function that sorts data is a classic example of the use of variable parameters. An unsorted set of numbers is passed to the function and a nicely sorted list is returned to the calling program segment. You may question the practicality of sorting only three numbers, but right now the thought is what counts. Sorting numbers is such a nice example of using two-way parameters and real practical sorting requires features that will be introduced in later chapters. Right now we will keep it simple by sorting three numbers. By the way, note how program PROG1406.CPP cleverly uses the Swap function that we introduced in the previous program. In one program we use two void functions that use two-way parameter passing. Pretty impressive stuff. The actual code in function Sort can be altered. There are a variety of techniques to sort data. The sorting approach demonstrated works, and the main point of the function is to show an example of using two-way parameter passing. // PROG1406.CPP // This program sorts three numbers from smallest to largest. #include #include void Display(int A, int B, int C); void Swap(int &X, int &Y); void Sort(int &A, int &B, int &C); void main() { clrscr(); int N1,N2,N3; cout << "Enter three integers ===>> "; cin >> N1 >> N2 >> N3; Display(N1,N2,N3); Sort(N1,N2,N3); Display(N1,N2,N3); getch(); } void Display(int A, int B, int C) { cout << endl << endl; cout << A << " " << B << " " << C << endl; } void Sort(int &A, int &B, int &C) { if (A > B) Swap(A,B); if (C < A) Swap(A,C); if (B > C) Swap(B,C); } void Swap(int &X, int &Y) { int T; T = X; X = Y; Y = T; } PROG1406.CPP OUTPUT Enter three integers ===>> 3 1 2 3 1 2 1 2 3 14.3 One-Way, Two-Way and Mixed-Way Parameters The last chapter introduced one-way parameters. They are the value parameters that pass information from the function call to the function. No information can be returned or passed back via the value parameter. Return functions do return information, but not with the value parameter. This chapter introduced the new idea of using void functions with two-way parameters. Information is passed from the function call to the function and after some process - such as sorting - altered information is passed back to the program segment of the function call. All the program examples have used functions that either used value (one-way) parameters exclusively or used only variable (two-way) parameters exclusively. It is also possible to mix both types of parameters in one function. In this section we will look at a simple payroll program and observe what types of parameters will be used in each function. Along the way, you will note that it is very normal to have functions that use a combination of value and variable parameters. The payroll program will also demonstrate how to pass information in a program. You will note that all information that is passed between the functions is passed by parameters. Passing information with global variables was a temporary stage in your computer science development. As a matter of fact, it will be wise not to mention this stage to other computer science people. Many folks in the computer science community will be shocked that you were exposed to evil, global, information passing. Program PROG1407.CPP Main function The first step is to consider the main function of the payroll program. At this stage you decide what information is required to and from each function called in the main program function. At every stage, ask the question if the information will change or if the information is now fixed and will no longer be altered. That answer will determine if a parameter becomes a value or variable parameter. Do not turn the page too quickly. See if you can pick the proper parameters yourself. // PROG1407.CPP // This program computes a simple payroll. // It demonstrates value, reference and mixed parameters. void main() { int HoursWorked; // hours worked per week double HourlyRate; // payrate per hour double GrossPay; // pay before deductions double Deductions; // tax deductions double NetPay; // total take-home pay EnterData(HoursWorked,HourlyRate); CompGross(HoursWorked,HourlyRate,GrossPay); CompDeduct(GrossPay,Deductions); CompNet(GrossPay,Deductions,NetPay); DisplayData(HoursWorked,HourlyRate,GrossPay,Deductions,NetPay); } Payroll Program, Main Function and Prototypes The next level of our payroll program identifies the value and variable parameters. This is a good example of the virtue of using prototypes. Placing the prototypes ahead of the main function allows you to view the communication between the main function and the major functions in the program. This makes it easier to decide on the nature of each parameter. // PROG1407.CPP // This program computes a simple payroll.. // It demonstrates value, reference and mixed parameters. #include #include #include void EnterData(int &HW, double &HR); void CompGross(int HW, double HR, double &GP); void CompDeduct(double GP, double &DD); void CompNet(double GP, double DD, double &NP); void DisplayData(int Hw, double HR, double GP, double DD, double NP); void main() { int HoursWorked; // hours worked per week double HourlyRate; // payrate per hour double GrossPay; // pay before deductions double Deductions; // tax deductions double NetPay; // total take-home pay EnterData(HoursWorked,HourlyRate); CompGross(HoursWorked,HourlyRate,GrossPay); CompDeduct(GrossPay,Deductions); CompNet(GrossPay,Deductions,NetPay); DisplayData(HoursWorked,HourlyRate,GrossPay,Deductions,NetPay); } Function EnterData is called first. Parameters HoursWorked and HourlyRate will receive information entered at the keyboard in the EnterData function. This means that both parameters will change, and must be variable parameters. Student frequently have difficulty here. It is natural to view this situation as one-way parameter passing from the function to the calling segment. This is true in the useful information-passing-sense. It is not true in the technical sense. Once HoursWorked and HourlyRate are defined in the main function, some arbitrary value will be assigned in each memory location. This means that HW and HR start out with some unknown values. However, these unknown value do change. They vary and as such we must use variable parameters in this function. The next function call needs to compute the gross pay of the employee. This is done with function CompGross. In this function HW and HR will be used to compute the gross pay, GP. This means that HW and HR will not change and are value parameters. GP does change and must be a variable parameter. We now follow the same logic with function CompDeduct. This function takes the gross pay, GP, and computes deductions, DD, based on total earnings. GP now does change and is a value parameter. Deductions, DD, does change and becomes a variable parameter. You have figured it out, right? In function CompNet, the only parameter that changes is the net pay, NP. Parameters GP and DD are used to compute NP, and will not change. In the final function, DisplayData, every parameter is a value parameter. Only information is shown. Nothing is processed and nothing changes. General Parameter Selection Rule Programs, generally speaking, consist of input, process, and output type of functions. Parameters in an input function normally use two-way information passing with variable parameters. Parameters in a process function normally use mixed-way information passing with a combination of value and variable parameters. Parameters in an output function normally use one-way information passing with value parameters. The Complete Payroll Program Check out the complete payroll program now. In particular, trace the information as it is passed between the different program modules. Stick with this program until it is clear to you why parameters are value or why they are variable. // PROG1407.CPP // This program computes a simple payroll. // It demonstrates value, reference and mixed parameters. #include #include #include void EnterData(int &HW, double &HR); void CompGross(int HW, double HR, double &GP); void CompDeduct(double GP, double &DD); void CompNet(double GP, double DD, double &NP); void DisplayData(int Hw, double HR, double GP, double DD, double NP); void main() { int HoursWorked; // hours worked per week double HourlyRate; // payrate per hour double GrossPay; // pay before deductions double Deductions; // tax deductions double NetPay; // total take-home pay EnterData(HoursWorked,HourlyRate); CompGross(HoursWorked,HourlyRate,GrossPay); CompDeduct(GrossPay,Deductions); CompNet(GrossPay,Deductions,NetPay); DisplayData(HoursWorked,HourlyRate,GrossPay,Deductions,NetPay); } void EnterData(int &HW, double &HR) { clrscr(); cout << "Enter hours worked ===>> "; cin >> HW; cout << "Enter hourly rate ===>> "; cin >> HR; } void CompGross(int HW, double HR, double &GP) { GP = HW * HR; } void CompDeduct(double GP, double &DD) { double Tax; // federal tax double SS; // social security and medicare deductions Tax = GP * 0.15; SS = GP * 0.0765; DD = Tax + SS; } void CompNet(double GP, double DD, double &NP) { NP = GP - DD; } void DisplayData(int HW, double HR, double GP, double DD, double NP) { cout << endl << endl; cout << setprecision(2); cout << "Hours Worked: " << HW << endl; cout << "Hourly Rate: " << HR << endl; cout << "Gross Pay: " << GP << endl; cout << "Deductions: " << DD << endl; cout << "Net Pay: " << NP << endl; getch(); } PROGRAM PROG1407.CPP Enter hours worked ===>> 17 Enter hourly rate ===>> 6.85 Hours Worked: 17 Hourly Rate: 6.85 Gross Pay: 116.45 Deductions: 26.38 Net Pay: 90.07 14.4 Parameter Vocabulary We are not finished with this function business, but before we move on you need to have a good, solid grasp of the vocabulary used with parameters. You will be better prepared for future computer science courses, and my job will be simpler in future chapters when I can assume a strong foundation in parameter terminology. The complexity with parameter vocabulary is the result of two facts. First, there are a variety of names for the exact same concepts, and you need to be sure that you know all those names. It is silly to try to figure out the difference between two types of parameters that turn out to be the same ones. Second, parameters have two sets of names, which identify parameters according to location or according to the purpose of the parameters. Do not get flustered by this. Look at high school students and see how many names are given to the same person. First, let's name according to class. Fine, you are Sophomore, Junior or Senior. Second, let's name according to grades. This makes you an A, B, or C student. (computer science students do not fail) Third, let's name according to activities like band, tennis team, Mu Alpha Theta, JETS, student council, etc. And, let's not forget to name according to first name like Joe, Sue, Kathy, Tom, Mary, etc. The point here is that placing a label on a person depends on the category of the label. Naming Parameters According to Location Parameters do a poor job hiding. You will find them in only two locations. A parameter is either in a function call or a function heading. There is no other place that a parameter can visit. We wish to label these two locations. Parameters in a function call are actual parameters and parameters in a function heading are formal parameters. Naming Parameters According to Location A parameter in a function call is named actual parameter. Frequently, actual parameters are also called arguments. A parameter in a function heading is named formal parameter. Look at program PROG1405.CPP again. The program is incomplete. It is only necessary to look at the main function with the function calls and the proto-types, which are identical to the function headings. (do not sweat the semi-colons) // PROG1405.CPP // This program returns two altered parameters. // It demonstrates a swap function. #include #include void Display(int A, int B); void Swap(int &A, int &B); void main() { int N1 = 5, N2 = 7; clrscr(); Display(N1,N2); Swap(N1,N2); Display(N1,N2); getch(); } In this program, N1 and N2 are actual parameters. Both parameters are used in function calls Display and Swap. A and B are formal parameters, located in the functions headings. Technically, the formal parameters in Display and Swap are in a totally different scope. It is customary to give them the same name, because formal parameters A and B work with the same actual parameters N1 and N2. Naming Parameters According to Purpose Parameters exist for the purpose of passing information in a program. In that department we have different parameters. A parameter may only pass information in one direction. This is casually called a one-way parameter. More precisely, such a parameter is called a value parameter because the function is invoked call-by-value. This chapter introduced the fancy parameters that can pass information in both direction, which are casually called two-way parameters. The more precise terminology is variable parameter, or even more precisely preferred, reference parameter, because the function is invoked call-by-reference. The next section will explain why the name reference parameter is very appropriate. Naming Parameters According to purpose A parameter that passes information in one direction is called a one-way parameter, value parameter or call-by-value. A parameter that passes information in two directions ( so it appears) is called a two-way parameter, variable parameter, reference parameter or call-by-reference. If you look at program PROG1405.CPP, yet one more time, you will see a different story. Parameters A and B in function heading Display are value parameters. However, in function Swap parameters A and B each show a conspicuous ampersand (&), indicating them to be variable or reference parameters in this case. // PROG1405.CPP // This program returns two altered parameters. // It demonstrates a swap function. #include #include void Display(int A, int B); void Swap(int &A, int &B); void main() { int N1 = 5, N2 = 7; clrscr(); Display(N1,N2); Swap(N1,N2); Display(N1,N2); getch(); } Perhaps I can also explain why a formal parameter is called formal. In this case, formal has nothing to do with formal in the tuxedo sense. Think of it as the form of the parameter. Consider this: can you look at a function call and always determine by those actual parameters if information is passed by value or by reference? No, that is not possible in every case. If the actual parameter is a constant, you can conclude that it must be a value parameter, but otherwise you do not have a clue. The parameters in a function heading are another story. Here the parameters appear with or without the (&) operator. Without (&) means value parameter and with (&) means variable parameter. In other words, you can determine the form of the parameter in the function heading. Hence the term formal parameter. 14.5 Parameters, a Technical Explanation As a rule, precise, technical information tends to intimidate and scare away students who are new to computer science. The combination of new terminology, strange concepts, and complex ideas can be unnerving. There are also times when the very preciseness of a technological explanation is exactly what helps to clarify what is going on in a bewildering topic. Right now this is one of these moments. First, you need to take a closer look at memory locations. Since the early chapters you have been told that data is stored in memory. Different data occupies different amounts of memory, but somewhere the data is stored. With C++ it is possible to find the exact memory location where a variable stores its information. It can be done with the reference operator. In this chapter the & has been used to indicate that a parameter is a variable or reference parameter. You are about to learn a more technical side to the humble reference (&) operator. We can stick an & in front of any variable and display the memory of that variable. Program PROG1408.CPP defines four variables: 2 integers and 2 floats. The program first displays the values assigned to each variable, and then displays the memory location of each variable. // PROG1408.CPP // This program demonstrates memory locations of variables. #include #include void main() { int IntVar1 = 1000; int IntVar2 = 2000; float FloatVar1 = 3.14159; float FloatVar2 = 1.23456; clrscr(); cout << "IntVar1 is " << IntVar1 << endl; cout << "IntVar2 is " << IntVar2 << endl; cout << "FloatVar1 is " << FloatVar1 << endl; cout << "FloatVar2 is " << FloatVar2 << endl; cout << endl; cout << "Address of IntVar1 is " << &IntVar1 << endl; cout << "Address of IntVar2 is " << &IntVar2 << endl; cout << "Address of FloatVar1 is " << &FloatVar1 << endl; cout << "Address of FloatVar2 is " << &FloatVar2 << endl; getch(); } PROG1408.CPP OUTPUT IntVar1 is 1000 IntVar2 is 2000 FloatVar1 is 3.14159 FloatVar2 is 1.23456 Address of IntVar1 is 0x8f420ffe Address of IntVar2 is 0x8f420ffc Address of FloatVar1 is 0x8f420ff8 Address of FloatVar2 is 0x8f420ff4 This first memory example program demonstrates two things. First, you do notice the hexadecimal (base 16) memory addresses. It does prove that the reference (&) operator can provide us with memory addresses. Something else can be seen. C++ grabs adjacent memory locations, whenever possible. Observe the memory locations. The output helps to show that float occupies 4 bytes in memory and int reserves 2 bytes. The numbering sequence may seem odd at first. Do keep in mind that memory is allocated from a higher memory address down to lower addresses. Also keep in mind that in base 16 the (base 10) values 10, 11, 12, 13, 14, 15 are represented with a, b, c, d, e, and f. Frankly, for the purpose of this chapter, it matters little if you know base 16 from mushroom pizza. The program examples in this section display memory locations. Some memory addresses are the same number, indicating that they occupy the same location. Other memory addresses have different numbers, indicating that they are different locations. The most important concept to understand in this section is how it is possible for more than one identifier to store information in the same memory location. A significant consequence of two variables occupying the same memory location is that a change in the value of one variable simultaneously alters the value of the other variable. Perhaps you do not see any connection between parameter passing and memory locations, and that is fine. Right now we need to establish the ability of the (&) reference operator to display memory, as well as reference memory. The next program will get down to the nitty-gritty of parameter passing. Program PROG1409.CPP has an artificial function CheckMem. The function performs no purpose besides displaying the memory of its parameters. In this program we first display the memory locations of the main function variables. These are the memory locations of the actual parameters Ref1 and Val1. They have been named intentionally to represent reference and value parameters. In function CheckMem there are corresponding Ref2 and Val2 parameters that represent the local, formal parameters of the function. The memory locations of those parameters will also be investigated. Check the program source code and also check the output. Does the output make sense? If it does great, if not read on and fine tune your brain. // PROG1409.CPP // This program demonstrates parameter memory locations. #include #include void CheckMem(int &Ref2, int Val2); void main() { int Ref1; int Val1; clrscr(); cout << "Address of Ref1 is " << &Ref1 << endl; cout << "Address of Val1 is " << &Val1 << endl; CheckMem(Ref1,Val1); getch(); } void CheckMem(int &Ref2, int Val2) { cout << endl << endl; cout << "Address of Ref2 is " << &Ref2 << endl; cout << "Address of Val2 is " << &Val2 << endl; } PROG1409.CPP OUTPUT Address of Ref1 is 0x8f5a0ffe Address of Val1 is 0x8f5a0ffc Address of Ref2 is 0x8f5a0ffe Address of Val2 is 0x8f5a0ffa Variable Val1 has it own private memory location. The value stored at that memory location is copied to the memory location of Val2. Once this copy is made, all changes will be stored in a different memory location. The actual parameter (in the function call) and the formal parameter (in the function heading) each have their own memory location. Now notice that both Ref1 and Ref2 have the exact same memory location. The value of Ref1 is not passed to the function. A reference to the memory location of the actual parameter is passed. This is a tremendously important difference with the call-by-value-parameter-passing approach. The value of the actual (Ref1) parameter is not copied in the function. It is not even passed to the function. The only thing that the function gets is a reference to the memory location of the actual parameter. Perhaps the name variable parameter made sense because the parameter can change. Hopefully, this technical explanation shows that the name reference parameter is not only appropriate, it is more descriptive than variable parameter. From now on we shall use the name reference parameter. The Secret of Two-Way Parameter Passing Unveiled Now you can see how this all works. You have been fooled. There is no information passed to a function, altered, and then passed back. The approach is actually much simpler and very memory efficient. What do we want? We want any changes in the formal parameter to cause a change in the actual parameter. If the actual parameters are a list of unsorted numbers, then we want the formal parameters in the function to bring about a nicely sorted list. Once the process is accomplished we want the results back at the actual parameter site. Terrific, keep it simple, and arrange for actual and formal parameters to camp out at the same memory location. Now you start with some actual parameter value. This same value is known to the formal parameter. Why? They are at the same location. Great, we move on and do something to the formal parameter. The value of the formal parameter changes in memory. But wait, the actual parameter is at the same location and automatically gets changed also. This is very slick and it might have slipped right passed you. You see, sometimes it helps to be technical. It is normal to feel on shaky understanding ground at this stage. No problem, read on and work with this stuff. I promise that suddenly lights will become very bright. Scope Revisited Scope was explained in the last chapter. This scope business is not directly related to the reference parameters discussed in this chapter. However, the business at hand is to take a technical look at parameter passing. It will be helpful to also get a better understanding of information that is stored in a program without the use of parameters. The program that follows was shown in the last chapter to explain the scope of many X identifiers. Every identifier has the same name, X, but every identifier has a different scope. We will return to this program, and this time not display the value of each identifier, but the memory location of every X. The newly found reference operator will accomplish this task, and you will note four different memory locations for four X identifiers in four different scopes. // PROG1410.CPP // This program explains scope by looking at memory locations. #include #include int X = 100; void Function1(); void Function2(); void main() { clrscr(); cout << "Global X Memory = " << &X << endl; int X = 200; cout << endl; cout << "Main Function X Memory = " << &X << endl; Function1(); cout << endl; cout << "Main Function X Memory = " << &X << endl; Function2(); getch(); } void Function1() { int X = 300; cout << endl; cout << "Function1 X Memory = " << &X << endl; if (X == 300) { int X = 400; cout << endl; cout << "Very Local X Memory = " << &X << endl; } cout << endl; cout << "Function1 X Memory = " << &X << endl; } void Function2() { cout << endl; cout << "Global X Memory = " << &X << endl; } PROG1410.CPP OUTPUT Global X Memory = 0x8ee00094 Main Function X Memory = 0x8f4f0ffe Function1 X Memory = 0x8f4f0ff6 Very Local X Memory = 0x8f4f0ff4 Function1 X Memory = 0x8f4f0ff6 Main Function X Memory = 0x8f4f0ffe Global X Memory = 0x8ee00094 14.6 Test Your Parameter Knowledge This section has four quiz programs. Each program has a Guess function that looks very similar to the Guess functions in the other programs. Be assured that the functions are different and each program creates different output. It will be very easy for you to type in the program, check the result, and then with great bravado announce to the world the obvious answer. After all, anybody whose brain generates enough energy for body life support systems can figure out something so simple. This approach may be lovely for ego stroking but it does not check if you know what is happening. The answers to these programs are intentionally not shown. For once, you are encouraged not to check the programs. This should be an exercise in mental computing. Check each program out prior to the class where these programs will be discussed. Figure the answer out by tracing through the execution sequence. You are the computer. Your teacher will discuss the answers of each program with you in class. Do not get fooled by the simplicity of the programs. You need to understand parameters very well to get the correct answers to all four programs. So look at the programs carefully. Hopefully, these quiz programs will help to confirm or perhaps clear up some lingering confusion. // PROG1411.CPP // Quiz 1 #include #include void Guess1(int X, int Y) { X++; cout << "X = " << X << endl; Y++; cout << "Y = " << Y << endl; } void main() { clrscr(); int Number1 = 10; int Number2 = 20; Guess1(Number1,Number2); cout << "Number1 = " << Number1 << endl; cout << "Number2 = " << Number2 << endl; getch(); } Do not get fooled. The first few program examples are quite straight forward and hopefully every student will get the correct answer. The later quiz programs will become more challenging. The majority of students tend not to get all quiz programs correct. Be warned and be careful. It is trickier than it looks. // PROG1412.CPP // Quiz 2 #include #include void Guess2(int X, int Y) { X++; cout << "X = " << X << endl; Y++; cout << "Y = " << Y << endl; } void main() { clrscr(); int Number = 10; Guess2(Number,Number); cout << "Number = " << Number << endl; getch(); } // PROG1413.CPP // Quiz 3 #include #include void Guess3(int &X, int &Y) { X++; cout << "X = " << X << endl; Y++; cout << "Y = " << Y << endl; } void main() { clrscr(); int Number1 = 10; int Number2 = 20; Guess3(Number1,Number2); cout << "Number1 = " << Number1 << endl; cout << "Number2 = " << Number2 << endl; getch(); } // PROG1414.CPP // Quiz 4 #include #include void Guess4(int &X, int &Y) { X++; cout << "X = " << X << endl; Y++; cout << "Y = " << Y << endl; } void main() { clrscr(); int Number = 10; Guess4(Number,Number); cout << "Number = " << Number << endl; getch(); } 14.7 Parameters Without Identifiers The prototype of a function appears to be completely identical as the function heading that follows later, with the exception of a semicolon. You will now see that the difference can be greater. It is not necessary to use any identifier in a function prototype. At first this seems odd, but there is considerable logic to make sense of this feature. The purpose of a prototype is to warn the compiler that an identifier is going to be used of a certain function, and that the full details of that function will come later. The prototype does provide the compiler with the necessary parameter information. This parameter information is important because the compiler will check if the actual and formal parameters match correctly. This brings up an interesting point. The names of the actual and formal parameters do not need to be the same. It is required that the function call and the function heading contain the same number of parameters. Furthermore, it is required that these parameters have identical data types. In other words, the identifier of the formal parameters is not important at all in the prototype and it is strictly optional. Prototype Fact The identifiers of the parameters in a prototype are optional. Prototype void GrossPay(int HW, double HR, double &GP); can also be written as: void GrossPay(int, double, double &); This style is permitted, but it is not recommended. Using identifiers will make your program more readable. Program PROG1415.CPP is the earlier payroll program. This time the prototypes have no parameter identifiers. Note that the actual function implementations do use parameter identifiers. In this program the function implementation only use stubs. This simplifies matters and does not distract from the concept that is explained here. // PROG1415.CPP // This program demonstrates that prototypes do not require // parameter identifiers, only parameter data types and // an ampersand if a reference parameter is used. // Ignore all the warnings about not using variables. #include #include #include void EnterData(int &, double &); void CompGross(int, double, double &); void CompDeduct(double, double &); void CompNet(double, double, double &); void DisplayData(int, double, double, double, double); void main() { int HoursWorked; // hours worked per week double HourlyRate; // payrate per hour double GrossPay; // pay before deductions double Deductions; // tax deductions double NetPay; // total take-home pay EnterData(HoursWorked,HourlyRate); CompGross(HoursWorked,HourlyRate,GrossPay); CompDeduct(GrossPay,Deductions); CompNet(GrossPay,Deductions,NetPay); DisplayData(HoursWorked,HourlyRate,GrossPay,Deductions,NetPay); getch(); } void EnterData(int &HW, double &HR) { } void CompGross(int HW, double HR, double &GP) { } void CompDeduct(double GP, double &DD) { } void CompNet(double GP, double DD, double &NP) { } void DisplayData(int HW, double HR, double GP, double DD, double NP) { } APCS Examination Alert It is very likely that students will encounter function prototypes without parameter identifiers on the case study and/or the the AP Computer Science Examination. The remainder of this book will include examples of function prototypes that do not use parameter identifiers. This is done for the sake of familiarization and exam preparation. You are still recommended to write programs with matching identifiers. It is quicker, and it is more readable. You might wonder how it is possible that writing more is quicker. The reason has to do with block copying. You write the prototypes, complete with identifiers, block copy the whole lot, and paste them below the main function. Now you remove the semicolons, adds some parenthesis, and you have a stub program that compiles. This approach actual takes more time if you have to remove, or later add, the parameter identifiers. But then you knew that because I am sure you are a believer in block copying what can be block copied. Just be careful. I have made some considerable booboos in book writing and test writing by block copying. The efficiency of block copying also makes duplication of mistakes more efficient. 14.8 Inline Functions Program modularity was introduced many chapters ago, and you have no doubt benefited from the use of functions. In the process of using functions it is sometimes possible to create an ¡§unwanted¡¨ inefficiency. Calling a function takes more execution time than executing inline code. Now breaking a program into logical segments and calling each one of these segment will not cause a penalty in execution time that is even noticeable. It is another story when the same function is called thousands or even millions of times in a program. This situation can easily happen in nested loop structures, where deep inside the inner loop, functions are called a large number of times. C++ allows an interesting compromise. You can make a special bargain. I will trade you some memory for execution time. C++ can create inline functions. These functions place the actual function code in the place of the function call. The result is that the execution time penalty does not happen. This is very beneficial if you have few locations with a function call, but that function call is repeated numerous times inside a loop. On the other hand, if you have the same function call spread many, many times all over the program, you will then get a penalty in memory usage from all the code that is copied. Inline functions traditionally, are very small functions, and they are frequently written on one line of code. Do not confuse a function written on one line with an inline function. Program PROG1416.CPP has three functions that all occupy one line. // PROG1416.CPP // This program demonstrates small functions written on one line. // These are not "inline" functions. #include #include void Module1() { cout << "Module1" << endl; } void Module2() { cout << "Module2" << endl; } void Module3() { cout << "Module3" << endl; } void main() { clrscr(); Module1(); Module2(); Module3(); getch(); } PROG1416.CPP OUTPUT Module1 Module2 Module3 The secret of inline functions is the reserved word inline that must precede the function heading. Program PROG1417.CPP is almost identical to the previous program. It does produce the same output, but this time genuine inline functions are used. // PROG1417.CPP // This program demonstrates small functions written on one line // preceded by the inline reserved word. // These are "inline" functions. #include #include inline void Module1() { cout << "Module1" << endl; } inline void Module2() { cout << "Module2" << endl; } inline void Module3() { cout << "Module3" << endl; } void main() { clrscr(); Module1(); Module2(); Module3(); getch(); } PROG1417.CPP OUTPUT Module1 Module2 Module3 Industrial Strength C++ C++ includes many, many features. The language has evolved to be an industrial strength programming language, which allows professional programmers complete freedom in program development. This freedom allows you to write some very bizarre programs. Inline functions are one example. This feature is meant to be used for very short functions that are called very frequently. Program PROG1418.CPP demonstrates correct syntax with inline functions that use prototypes. Notice that the reserved word inline, only is used with the prototype, not the function implementation. This program also demonstrates that inline functions are not required to be one line. This example demonstrates proper syntax, and simultaneously it shows precisely when you should not be using inline functions. Each function is called only once in this case. There is nothing useful gained in this situation to use inline functions. In some later chapters I will present a variety of practical program algorithms that call the same functions very frequently. For now, be happy with the syntax. // PROG1418.CPP // This program demonstrates a variety of inline functions. // It also demonstrates that inline functions are not based on // one line but the inline reserved word. #include #include inline void EnterData(int &N1, int &N2); inline int AddData(int N1, int N2); inline void DisplayData(int N1, int N2, int Sum); void main() { int Nbr1,Nbr2; EnterData(Nbr1,Nbr2); DisplayData(Nbr1,Nbr2,AddData(Nbr1,Nbr2)); } void EnterData(int &N1, int &N2) { clrscr(); cout << "Enter two integers ===>> "; cin >> N1 >> N2; } int AddData(int N1, int N2) { return N1 + N2; } void DisplayData(int N1, int N2, int Sum) { cout << endl << endl; cout << N1 << " + " << N2 << " = " << Sum << endl; getch(); } PROG1418.CPP OUTPUT Enter two integers ===>> 12 13 12 + 13 = 25 14.9 Overloaded Functions This section title should look very suspicious. ¡§Overloading¡¨ a function sounds pretty strange. Is the function too large? Does the function process too much? Does it require too much memory? These are logical questions, but they are totally unrelated to the topic at hand. Even though we are not jumping into Object Oriented Programming (OOP) yet, the topic of overloading is one of the important elements in OOP. Let us start by considering some function shortcomings. Imagine that you have the need to swap data. Not just one type of data, but integers, characters and real numbers. The knowledge that you have right now should lead you to a program style similar to the one shown in program PROG1419.CPP. Three different functions are created to swap three different types of data. Function Swap1 exchanges integers, function Swap2 exchanges characters, and function Swap3 takes care of real numbers. // PROG1419.CPP // This program swaps three different data types with three // different swap functions. #include #include void Swap1(int &, int &); void Swap2(char &, char &); void Swap3(double &, double &); void main() { int IVar1 = 10, IVar2 = 20; char CVar1 = 'A', CVar2 = 'Z'; double DVar1 = 1.111, DVar2 = 9.999; clrscr(); cout << "Integers: " << IVar1 << "\t\t" << IVar2 << endl; cout << "Characters: " << CVar1 << "\t\t" << CVar2 << endl; cout << "Doubles: " << DVar1 << "\t" << DVar2 << endl; Swap1(IVar1,IVar2); Swap2(CVar1,CVar2); Swap3(DVar1,DVar2); cout << endl; cout << "Integers: " << IVar1 << "\t\t" << IVar2 << endl; cout << "Characters: " << CVar1 << "\t\t" << CVar2 << endl; cout << "Doubles: " << DVar1 << "\t" << DVar2 << endl; getch(); } void Swap1(int &I1, int &I2) { int Temp; Temp=I1; I1=I2; I2=Temp; } void Swap2(char &C1, char &C2) { char Temp; Temp=C1; C1=C2; C2=Temp; } void Swap3(double &D1, double &D2) { double Temp; Temp=D1; D1=D2; D2=Temp; } This style of programming works, and we can add more Swap functions as the need arises. It will become tedious to keep track of the many different functions that all perform the same action. There is an improvement in clarity by changing the function names to SwapInt, SwapChar and SwapDouble, but all these multiple names are quite tedious. Would it not be lovely if we could just call one function - with the simple name Swap - and be done with the problem? This capability exists in C++, and we can accomplish the desired - single name identifier - by using overloading. So what is overloading? It has nothing to do with loading really. Overloading enables you to use the same program features for different purposes. This can apply to operators and it can apply to functions. You have already used this business with operators, and you did not give it a second thought. Consider the following two statements: 100 + 100 = 200 (arithmetic addition) ¡¨100¡¨ + ¡¨100¡¨ = ¡¨100100¡¨ (string concatenation) The same ( + ) operator is used for arithmetic addition and also for string concatenation. Computer science people would say: ¡§The + operator is overloaded.¡¨ Overloading Overloading enables you to use the same program features for different purposes. In C++ operators and functions can be overloaded. Understanding the basic overloading principle is a prerequisite to learning Object Oriented Programming (OOP) Overloading Example: int X,Y; apstring First,Last,Complete; X = 100; Y = 200; Sum = X + y; First = ¡¨Kathy¡¨; Last = ¡¨Denver¡¨; Complete = First + Last; In this example the plus (+) operator is overloaded for arithmetic addition and for string concatenation. Program PROG1420.CPP uses three functions that exchange integers, characters and real numbers. Each one of the functions has the same identifier Swap. // PROG1420.CPP // This program swaps three different data types with three // different swap functions that all have the same name. #include #include void Swap(int &, int &); void Swap(char &, char &); void Swap(double &, double &); void main() { int IVar1 = 10, IVar2 = 20; char CVar1 = 'A', CVar2 = 'Z'; double DVar1 = 1.111, DVar2 = 9.999; clrscr(); cout << "Integers: " << IVar1 << "\t\t" << IVar2 << endl; cout << "Characters: " << CVar1 << "\t\t" << CVar2 << endl; cout << "Doubles: " << DVar1 << "\t" << DVar2 << endl; Swap(IVar1,IVar2); Swap(CVar1,CVar2); Swap(DVar1,DVar2); cout << endl; cout << "Integers: " << IVar1 << "\t\t" << IVar2 << endl; cout << "Characters: " << CVar1 << "\t\t" << CVar2 << endl; cout << "Doubles: " << DVar1 << "\t" << DVar2 << endl; getch(); } void Swap(int &I1, int &I2) { int Temp; Temp=I1; I1=I2; I2=Temp; } void Swap(char &C1, char &C2) { char Temp; Temp=C1; C1=C2; C2=Temp; } void Swap(double &D1, double &D2) { double Temp; Temp=D1; D1=D2; D2=Temp; } PROG1420.CPP OUTPUT 10 20 A Z 1.111 9.999 20 10 Z A 9.999 1.111 It is quite possible that the overloaded function business leaves you wondering about all the hype. It sounds good, but the bottom line is that you still have to write as many swap functions as there are data types to exchange. This true, but it certainly should be nice that no matter what gets swapped, the same Swap identifier takes care of the job. This gets us to the main point. How about duplicate identifier errors? It is easy to convince yourself that this will not happen by running the program example. The C++ compiler has no difficulty determining which function is called. The secret is in parameter matching. Each Swap function has a different set of parameters and choice of actual parameters in the function call determines where the program execution continues. The point of this overloaded-match-by-parameters will be illustrated with the next program example. Program PROG1421.CPP does not accomplish anything. It is a short program that displays three different messages. The program also is kind of bizarre. Function Display seems to have a heading with a parameter - at least a type for a parameter - but the function body does not use any parameters at all. Is it very strange or does it makes sense to you? The main function of the program will be shown first with the output. You will note that the same function (at least same function identifier) is called three times with three different actual parameter types. Three different messages result. PROG1421.CPP MAIN FUNCTION AND OUTPUT void main() { clrscr(); Display(1); Display('A'); Display(1.1); getch(); } INTEGER DISPLAY CHARACTER DISPLAY DOUBLE DISPLAY // PROG1421.CPP // This program displays three different messages depending // on the parameters provided in the display function. #include #include void Display(int); void Display(char); void Display(double); void main() { clrscr(); Display(1); Display('A'); Display(1.1); getch(); } void Display(int) { cout << endl << endl; cout << "INTEGER DISPLAY" << endl; } void Display(char) { cout << endl << endl; cout << "CHARACTER DISPLAY" << endl; } void Display(double) { cout << endl << endl; cout << "DOUBLE DISPLAY" << endl; } What you see here is an interesting different use of function parameters. In this case, information is not passed to a function for any type of process. The only job of the actual parameter is to help identify which Display function will be executed next. The main reason for showing this program is to help understand how overloaded functions can be properly selected. A secondary reason is to show a parameter capability that will show up in a later OOP chapter for a specific purpose. It will help in the future chapter if this function ability already has been introduced, even if very briefly. 14.10 Function Parameter Rules There have been two chapters of parameter functions and a very heavy chunk of information has been thrown at you. It is time to summarize this parameter function business with a variety of rules. These rules are numbered only for reference. The rule numbers have no relationship to any rule numbers that exist in the computer science community or in C++. Function Parameter Rule #1 The number of actual and formal parameters must match. Example: void Rule1(int X, int Y, int Z) { } Rule1(10,20,30); Function Parameter Rule #2 The data types of the actual and formal parameters must match according to their position in the function call and the function heading. Example: void Rule2(int X, char Y, double Z) { } Rule2(100,¡¦A¡¦,123.321); Function Parameter Rule #3 Value parameters can use constants, expressions, or variables for actual parameters. Example: void Rule3(int X, int Y, int Z) { } int Number = 500; Rule3(100,200+300,Number); Function Parameter Rule #4 Variable parameters may only use variables for actual parameters, Example: void Rule4(int &X, char &Y) { } int Number = 500; char Letter = ¡¦Q¡¦; Rule4(Number,Letter); Function Parameter Rule #5 The use of parameter identifiers is optional in function prototypes. void Rule5(int, char, double); int Number = 500; char Letter = ¡¦Q¡¦; double Real = 123.321; Rule5(Number,Letter,Real); void Rule5(int N, char L, double R) { } Note: You may also write function implementations without parameters if the intention is to use overloaded functions with different signatures, and the parameter is not used in the function body. Function Parameter Rule #6 The formal parameter in a function determines if the information will be passed by value or by reference. The use of an ampersand (&) next to the data type of a formal parameter indicates a reference parameter. void Rule6(int, char &, double &); int Number = 500; char Letter = ¡¦S¡¦; double Real = 123.321; Rule6(Number,Letter,Real); void Rule6(int N, char &C, double &R) { } Function Parameter Rule #7 The identifiers of actual parameters and their corresponding formal parameters can be identical or different. void Rule7(apstring Tom, apstring Sue); apstring Tom, Sue; apstring Kathy, George; Rule7(Tom,Sue); Rule7(Kathy, George); Many people believe that actual and formal parameters should intentionally use different names, even if it is not required by the C++ compiler. This summary of rules is very important to remember, and more important to apply in practice. Any time my students asks for help on some logic problem, I always check their parameters first. You will be surprised at the large number of programs that are totally finished - at least the students thinks so - with multiple parameter errors. The Track Relay Analogy Let us summarize this parameter business, with a real life analogy that may help some students. The analogy that follows explains some of the parameter rules in a totally different manner. Imagine that you are at a track meet and you watching a relay race. In this relay race the starters run 100 meters and then pass a baton to the next runner in their team. In the first relay race example, the race official checks the track and announces that the race is not ready. A look at Race1 shows there are four starters ready in their lanes, but only three runners at the 100 meter baton passing mark. A runner from the Netherlands (NL) is missing. Race1 Race2 presents another situation with a different problem. This times the number of runners is correct. There are four starters and there are also four runners at the 100 meter mark ready to receive a baton. However two runners at the 100 meter mark are standing in the wrong lane. The track official announces that the race cannot start unless the runners change lanes and are ready to receive the batons from their own country men. Race2 Race3 is not a problem situation. This race demonstrates an analogy to help explain the naming of actual and formal parameters. In Race3, runner John starts for the United States (US) and passes to William. George starts for Great Britain (GB) and passes to Thomas. Gerard starts for France (FR) and passes to Louis. Finally, Hans starts for the Netherlands and passes to another Hans. Race3 The point of this analogy is that the names do not matter. What matters is that there are the same number of runners at the passing mark as there are in the starting blocks. It also matters that everybody stays in their lanes and that the runners receiving batons are on the same team as the starters. The batons are passed not based on the names of the runners, but on the lanes they run in. 14.11 Worked-Out Exercises Make the same assumptions as done with previous worked-out exercises. The functions presented are part of a complete program. All necessary function libraries are included in the program and all necessary variables are declared. Each function starts with a correct function call from somewhere in a theoretical program. All the functions in this section are void functions. The concern with each function is the resulting value of the parameter(s) in the function call. A function with appropriate parameters is provided with each exercise. In the trace table an & is placed in front of any reference parameter, and the variable of the function call that references the same memory address is listed in parenthesis. Exercise 01 void Ex1401(int &N) { for (int K = 1; K < 5; K++) if (N % 2 == 0) N++; else N+=3; } int P = 4; Ex1401(P); P K N % 2 == 0 &N (P) 4 4 1 true 5 2 false 8 3 true 9 4 false 12 After Function Call P = 12 Exercise 02 void Ex1402(int &X, int Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; int Q = 6; Ex1402(P,Q); P Q &X (P) Y 5 6 5 6 15 21 After Function Call P = 15 Q = 6 Exercise 03 void Ex1403(int X, int &Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; int Q = 6; Ex1403(P,Q); P Q X &Y (Q) 5 6 5 6 15 21 After Function Call P = 5 Q = 21 Exercise 04 void Ex1404(int &X, int &Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; int Q = 6; Ex1404(P,Q); P Q &X (P) &Y (Q) 5 6 5 6 15 21 After Function Call P = 15 Q = 21 Exercise 05 void Ex1405(int &X, int Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; Ex1405(P,P); P &X (P) Y 5 5 5 15 20 After Function Call P = 15 Exercise 06 void Ex1406(int X, int &Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; Ex1406(P,P); P X &Y (Q) 5 5 5 15 20 After Function Call P = 20 Exercise 07 void Ex1407(int &X, int &Y) { X += 10; cout << "X = " << X << endl; Y += X; cout << "Y = " << Y << endl; } int P = 5; Ex1407(P,P); P &X (Q) &Y (Q) 5 15 15 30 30 After Function Call P = 30



Chapter XII Control Structures II Chapter XII Topics 12.1 Introduction 12.2 The For Loop Revisited 12.3 Using the Best Loop Structure 12.4 Nested Selection 12.5 Nested Looping 12.6 Compound Conditions 12.7 Program Input Protection 12.8 Short-Circuiting Compound Decisions 12.9 Recursion, a Sneak Preview 12.10 Worked-Out Exercises 12.1 Introduction You knew that this chapter was coming. Anytime that a chapter is titled Control Structures I, there is guaranteed to come a sequel. You did not exactly have to wait very long for this sequel. Hopefully, the Boolean chapter has given you some tools that can be applied in this chapter. Frankly, the Boolean chapter by itself is difficult to justify. But in the area of complex control structures, knowledge of Boolean logic is very useful. This chapter introduces a variety of advanced control structure features. We start by looking at some specialized features of the for loop structure. The next concern is to see how control structures can be nested inside each other. Then comes the compound condition section which draws heavily on the previous Boolean logic chapter. The chapter then concludes with some practical input protection program examples that also require understanding of Boolean logic and a brief section on short-circuiting compound decisions. When you are finished with this chapter, you will be able to start writing some rather complex programs that are both challenging and interesting. Simple sequence programs, and even programs with simple conditions, lack the flexibility that you can create with the C++ programming tools shown in this chapter. 12.2 The For Loop Revisited The for loop has many, many different capabilities. It is possible to create some real convoluted code, which will probably invoke the wrath of thy teacher. Try not to be too clever with obscure code that works. Be clever with clean, simple, readable code that works. Anyway, I digress. It is important to realize that multiple variables can be handled by the for loop at the same time. This concept is planted as a seed at this stage. Why a seed, and not a civilized plant? The really practical examples for such multiple variables requires additional concepts that are introduced later. This for concept is shown now with the control structures chapter, and later you will see some good use. Program PROG1201.CPP shows that the for loop initializes two variables, and alters two variables. The K variable counts up, and the L variable counts down. Serious excitement is lacking from this program example, but at least you get a general flavor for some of the exotic capabilities of the for loop. // PROG1201.CPP // This program demonstrates multiple variables in a for loop. #include #include void main() { int K,L; clrscr(); for (K=1,L=5; K <= 5; K++,L--) cout << "K = " << K << " L = " << L << endl; getch(); } PROG1201.CPP OUTPUT K = 1 L = 5 K = 2 L = 4 K = 3 L = 3 K = 4 L = 2 K = 5 L = 1 I will now make a modest attempt to show a program that makes use of the double variable feature available with the for loop structure. The next program example will not only count the quarters, but also add the value of the growing number of quarters. One variable, Count, counts the quarters, and a second variable, Value, adds the monetary values of the coins. Note how this program also demonstrates that it is possible to use an int loop variable and a double loop variable in the same loop control structure. // PROG1202.CPP // This program demonstrates a practical example of using multiple // variable of a for loop. This program counts the number of // quarters, as well the value of the quarters. #include #include void main() { int Count; double Value; clrscr(); for (Count = 1, Value = 0.25; Count <= 20; Count++,Value+=0.25) cout << Count << " Quarter(s) equals " << Value << endl; getch(); } PROG1202.CPP OUTPUT 1 Quarter(s) equals 0.25 2 Quarter(s) equals 0.5 3 Quarter(s) equals 0.75 4 Quarter(s) equals 1 5 Quarter(s) equals 1.25 6 Quarter(s) equals 1.5 7 Quarter(s) equals 1.75 8 Quarter(s) equals 2 9 Quarter(s) equals 2.25 10 Quarter(s) equals 2.5 11 Quarter(s) equals 2.75 12 Quarter(s) equals 3 13 Quarter(s) equals 3.25 14 Quarter(s) equals 3.5 15 Quarter(s) equals 3.75 16 Quarter(s) equals 4 17 Quarter(s) equals 4.25 18 Quarter(s) equals 4.5 19 Quarter(s) equals 4.75 20 Quarter(s) equals 5 12.3 Using the Best Loop Structure You have been introduced to all the control structures, but hardly to all the nifty features of all the control structures. However, each one of the basic loop and selection structures have been introduced. More advanced processing with control structures is just around the block. Right now we want to stop for a moment and decide which loop to use. It is great that C++ supplies us with three types of loops, but when do you use which loop? When to Use the for Loop The for loop is ideal for loop structures that repeat some process a known - or fixed - number of times. Program PROG1203.CPP uses a for loop to compute the average of a set of grades. Assume in this case that the quantity of the grades is known. Such a situation certainly occurs in most classrooms with a fixed number of students. The program example requests the number of grades to be averaged and then the loop structure accumulates the grades on a growing sum. After the loop is finished, the average or Mean is computed by dividing the Sum by the Count of the grades. You may think that the loop is not fixed because you can specify the count of the grades. Fixed means that the iterations of the loop are known before the loop starts. This is precisely the case with the program example shown on the next page. Please note that the accumulator, Sum, is initialized to 0. This small part is frequently forgotten and causes many logic problems in a program. Initialize Your Accumulators Program loop structures frequently use variables that sum-up entered values. Such a variable is called an accumulator. Make sure that the accumulator is initialized before it is used inside the loop structure. // PROG1203.CPP // This program computes the average for a fixed set of grades // using a for loop. #include #include void main() { int K; // loop counter int Count; // quantity of numbers to be average int Grade; // individual grade entered from the keyboard int Sum = 0; // sum of the grades double Mean; // grade average clrscr(); cout << "How many grades will be averaged ===>> "; cin >> Count; for (K = 1; K <= Count; K++) { cout << "Enter grade ===>> "; cin >> Grade; Sum += Grade; // accumulator } Mean = (double) Sum/Count; cout << endl; cout << "Grade average: " << Mean << endl; getch(); } PROG1203.CPP OUTPUT How many grades will be averaged ===>> 5 Enter grade ===>> 60 Enter grade ===>> 70 Enter grade ===>> 80 Enter grade ===>> 90 Enter grade ===>> 100 Grade average: 80 You will note that the for loop did a nice job averaging our numbers. This type of situation is ideal for the for loop. The number of repetitions is fixed and fixed repetitions are performed very easily with C++¡¦s for loop structure. Now let us keep going with this average business, but alter the scenario. It is now college time in a Freshman history class. Roughly 600 students are in the same class, and they have just finished taking the midterm exam. The instructor needs the class average to decide on the curve. The class size cannot be assumed, even though the total number of students enrolled is known. It is very likely that out of 600 students more than 20 have missed the exam because of illness or dropping the class. The instructor is not enthusiastic to first count all the scantron sheets and then handle the same scantrons a second time for grade entry. There must be a better way. Why not let the computer do the counting? The trick with such a system is to use some type of flag to let the computer know when the loop is finished. The instructor decides to use -999. Such a number is a very unlikely grade for any student and the number is easy to remember. In other words, grades will be entered until -999 is entered and then the loop stops. This certainly is not a fixed loop situation. The loop required will be conditional. The condition changes when the value -999 is entered. In this situation it is better to use either the while loop or the do...while loop. Either loop is ideal for a conditional exit. You will note in the following program example that a counter must be used as well as the accumulator, and both must be initialized. Also note that Grade is initialized, which is necessary to enter the loop properly for the first time. // PROG1204.CPP // This program computes the average for an unknown quantity of // grades using a while loop. #include #include void main() { int K = 0; // loop counter int Grade = 0; // individual grade entered from the keyboard int Sum = 0; // sum of the grades double Mean; // grade average clrscr(); while (Grade != -999) { cout << "Enter grade ===>> "; cin >> Grade; K++; // counter Sum += Grade; // accumulator } Mean = (double) Sum/K; cout << endl; cout << "Grade average: " << Mean << endl; getch(); } PROG1204.CPP OUTPUT Enter grade ===>> 60 Enter grade ===>> 70 Enter grade ===>> 80 Enter grade ===>> 90 Enter grade ===>> 100 Enter grade ===>> -999 Grade average: -99.833333 The first conditional loop example is intentionally wrong. A very common - yet subtle - logic error is introduced. The average should be 80 and the answer is negative. The while loop body shows the problem. Every grade that is entered is counted and accumulated. Remember the all-important-big-deal counter and accumulator. Our clever flag to exit, -999, is counted as one of the grades, and is also accumulated to the total sum. This is not the intention of an average program. Actually, programs have few intentions, but we like to think that programmers intend for programs to work correctly. There are various approaches to solving this problem. One approach is shown with program PROG1205.CPP. A special if statement (as shown in the program segment below) is used to make sure that only grades are counted and accumulated if they are not -999. if (Grade != -999) { K++; Sum += Grade; } Also keep in mind that the braces are absolutely vital. You want both the statements that count and accumulate to be controlled by the if condition. With this special segment added, the program works as expected. // PROG1205.CPP // This program computes the average for an unknown quantity of // grades using a while loop, and fixes the logic problem #include #include void main() { int K = 0; // loop counter int Grade = 0; // individual grade entered from the keyboard int Sum = 0; // sum of the grades double Mean; // grade average clrscr(); while (Grade != -999) { cout << "Enter grade ===>> "; cin >> Grade; if (Grade != -999) { K++; Sum += Grade; } } Mean = (double) Sum/K; cout << endl; cout << "Grade average: " << Mean << endl; getch(); } PROG1205.CPP OUTPUT Enter grade ===>> 60 Enter grade ===>> 70 Enter grade ===>> 80 Enter grade ===>> 90 Enter grade ===>> 100 Enter grade ===>> -999 Grade average: 80 The same averaging - in a conditional way - can be done with the do...while loop. This postcondition loop also needs to make sure that the flag does not get counted nor accumulated. Strictly for the sake of the average program, there does not appear to be much difference between while and do...while. // PROG1206.CPP // This program computes the average for an unknown quantity of // grades using a do...while loop. #include #include void main() { int K = 0; // loop counter int Grade = 0; // individual grade entered from the keyboard int Sum = 0; // sum of the grades double Mean; // grade average clrscr(); do { cout << "Enter grade ===>> "; cin >> Grade; if (Grade != -999) { K++; Sum += Grade; } } while (Grade != -999); Mean = (double) Sum/K; cout << endl; cout << "Grade average: " << Mean << endl; getch(); } PROG1206.CPP OUTPUT Enter grade ===>> 60 Enter grade ===>> 70 Enter grade ===>> 80 Enter grade ===>> 90 Enter grade ===>> 100 Enter grade ===>> -999 Grade average: 80 When to Use the while and do...while Loop The while loop and do...while loop are ideally used for repetition that depends on a certain condition to be true. This is not necessarily a counting condition. The loop may exit when a certain value is entered. Flag is the term used in computer science to detect if a certain value is entered or altered to change the loop exit condition. The value that is entered is called the Flag. Essentially, a case has been made for fixed iteration and conditional iteration. The C++ tool of choice for fixed iteration is the for loop. The C++ tools for conditional iteration are the while loop and do...while loop. This should leave the nagging question about the need for two conditional loops. Apparently, they both do the same job. In the averaging example, you will not observe the need for two conditional loop structures. Once again, this is an example where more time will be needed to truly see the difference. Right now consider the following. The do...while loop forces a minimum of one program execution. This situation is precisely what you want for a program that checks someone¡¦s password or any other input that must be done at least once. The while loop does not necessarily execute at all. This is also desirable. A program that is designed to retrieve the contents of a file and display them, should not do anything if the file is totally empty. This explanation is very skimpy. Good program examples require more sophisticated C++ knowledge. That will come. Right now, in this chapter you need to be concerned with the proper syntax and some fundamental differences between the loop structures. 12.4 Nested Selection Chapter 10 was so nice and clean. Selection structures and loop structures were uncomplicated. There were no compound conditions, and there certainly was nothing called ¡§nesting.¡¨ Nesting a structure actually is not that bad. Essentially, it means that a conditional statement is nested inside another conditional statement. The program example below starts with one conditional statement to determine if a bonus will increase for exceeding a sales quota. Now the generous boss decides to add yet another bonus for years worked for the company. There is a catch; this extra bonus is only for employees who meet the yearly sales goal. This means that the conditional statement for ¡§years worked¡¨ needs to be placed inside the condition for meeting the sales quota. // PROG1207.CPP // This program demonstrates nested one-way selection. #include #include void main() { float Bonus = 500; float Sales; int Years; clrscr(); cout << "Enter your yearly sales ===>> "; cin >> Sales; cout << endl; if (Sales >= 250000) { cout << "Your bonus will be increased by $1000.00" << endl; Bonus += 1000; cout << "Enter years worked with the company ===>> "; cin >> Years; if (Years > 10) { cout << "Your bonus will be increased by $250.00" << endl; Bonus += 250; } } cout << endl; cout << "Your year-end bonus will be: " << Bonus; getch(); } PROG1207.CPP OUTPUT #1 Enter your yearly sales ===>> 300000 Your bonus will be increased by $1000.00 Enter years worked with the company ===>> 15 Your bonus will be increased by $250.00 Your year-end bonus will be: 1750 PROG1207.CPP OUTPUT #2 Enter your yearly sales ===>> 500000 Your bonus will be increased by $1000.00 Enter years worked with the company ===>> 5 Your year-end bonus will be: 1500 PROG1207.CPP OUTPUT #3 Enter your yearly sales ===>> 100000 Your year-end bonus will be: 500 Program PROG1208.CPP is another nested selection example. This time the if...else is nested. In this program example, a student is admitted based on SAT performance. There is also the question of financial aid. For this program, financial aid is determined by family income. However, it is not necessary to consider financial aid unless the student is admitted. The solution is to nest the financial aid condition inside the college admission condition. When you look closely at the nested control structure examples, you will note that nothing really new is introduced. The syntax and the logic of the conditional statements are the same. Using braces with multiple statements is also the same. The only difference is an appearance of complexity because you see one conditional statement inside another control structure. Anytime that you are bothered by complexity, focus on a smaller, less complex part. Digest only what you can handle and computer science becomes much easier. This is important advice because programs in the real world and in the second year course are considerably more complex than the program examples and assignments that you are doing currently. This is fine. When you get to the more complex programs, you will be ready for them. // PROG1208.CPP // This program displays an admission message based on an entered // SAT score. It also determines financial need with a nested // if...else structure. #include #include void main() { int SAT; float Income; clrscr(); cout << "Enter your SAT score ===>> "; cin >> SAT; cout << endl; if (SAT >= 1100) { cout << "You are admitted" << endl << endl; cout << "Enter your family income ===> "; cin >> Income; if (Income <= 20000) cout << endl << "You will receive financial aid" << endl; else cout << endl << "You will not receive financial aid" << endl; } else { cout << endl << "You are not admitted" << endl; } getch(); } PROG1208.CPP OUTPUT #1 Enter your SAT score ===>> 1500 You are admitted Enter your family income ===>> 10000 You will receive financial aid PROG1208.CPP OUTPUT #2 Enter your SAT score ===>> 1200 You are admitted Enter your family income ===>> 75000 You will not receive financial aid PROG1208.CPP OUTPUT #3 Enter your SAT score ===>> 800 You are not admitted The next program example uses multiple nested if...else statements to control multiple selections. Now switch is specifically designed to handle multiple selection. True, but switch has limitations. Consider the situation that is illustrated with the next program example. You need to convert numerical scores to a letter grade. Using switch requires a precise value. Now realize that every value in the 90.000 ... 100.000 range becomes an A. It is not possible to handle that by using switch. If you are willing to use an incredibly large number of cases, you might argue that switch works. Even if you are willing to go that route, C++ will not except the use of a real number for a switch variable. The logical course of action is to use the multiple nested if...else statements that are shown in program PROG1209.CPP. Please note the special indentation style that is traditionally used with multiple if...else statements. Lining up all the if statements is both readable and it makes it easier to fit the statements on the screen and on paper. // PROG1209.CPP // This program assigns grades 'A'..'F' based on numerical scores // using multiple nested if..else statements. #include #include void main() { float Score; char Grade; clrscr(); cout << "Enter your numerical score ===>> "; cin >> Score; if (Score >= 90.0) Grade = 'A'; else if (Score >= 80.0) Grade = 'B'; else if (Score >= 70.0) Grade = 'C'; else if (Score >= 60.0) Grade = 'D'; else Grade = 'F'; cout << "Your grade will be: " << Grade << endl; getch(); } PROG1209.CPP OUTPUT #1 Enter your numerical score ===>> 100 Your grade will be: A PROG1209.CPP OUTPUT #2 Enter your numerical score ===>> 90.5 Your grade will be: A PROG1209.CPP OUTPUT #3 Enter your numerical score ===>> 83.756 Your grade will be: B PROG1209.CPP OUTPUT #4 Enter your numerical score ===>> 79.999 Your grade will be: C PROG1209.CPP OUTPUT #5 Enter your numerical score ===>> 60 Your grade will be: D PROG1209.CPP OUTPUT #6 Enter your numerical score ===>> 59.999 Your grade will be: F 12.5 Nested Looping Decision structures are not the only control structures that can be nested. The loop structures can be nested as well. Each one of the three loop structures will be shown with the same type of program. The mission of each program is to display a variety of multiplication tables. It requires one loop structure to display a multiplication table, and it takes another loop structure to display additional multiplication tables. The output of each program is identical, and shown below. PROG1210.CPP, PROG1211.CPP & PROG1212.CPP OUTPUT 1 * 11 = 11 2 * 11 = 22 3 * 11 = 33 4 * 11 = 44 5 * 11 = 55 1 * 12 = 12 2 * 12 = 24 3 * 12 = 36 4 * 12 = 48 5 * 12 = 60 1 * 13 = 13 2 * 13 = 26 3 * 13 = 39 4 * 13 = 52 5 * 13 = 65 // PROG1210.CPP // This program displays several multiplication tables using // nested for loop structure #include #include void main() { int K; int Table; clrscr(); for (Table = 11; Table <= 13; Table++) { for (K = 1; K <= 5; K++) cout << K << " * " << Table << " = " << K * Table << endl; cout << endl; } getch(); } // PROG1211.CPP // This program displays several multiplication tables using // nested while loop structure. #include #include void main() { int K; int Table; clrscr(); Table = 11; while (Table <= 13) { K = 1; while (K <= 5) { cout << K << " * " << Table << " = " << K * Table << endl; K++; } cout << endl; Table++; } getch(); } // PROG1212.CPP // This program displays several multiplication tables using // nested do...while loop structure. #include #include void main() { int K; int Table; clrscr(); Table = 11; do { K = 1; do { cout << K << " * " << Table << " = " << K * Table << endl; K++; } while (K <= 5); cout << endl; Table++; } while (Table <= 13); getch(); } Nested looping shows a pattern that was seen earlier with nested decision structures. The syntax, and logic are identical to a single loop. The for loop is probably the easiest loop for nesting. The while loop can be tricky, if you are not careful about initializing the loop condition variables. Consider the following program segment EXAMPLE1. // EXAMPLE 1 Row = 0; Col = 0; while (Row < 10) { Row++; while (Col < 10) { Col++; cout << Row << ¡¨ , ¡¨ << Col << endl; } cout << endl; } Both Row and Col are giving the initial values of 0. These values are assigned before any loop body is entered. The problem is that after the inner loop is executed once, it will not execute a second time. The second time that the outer loop with Row executes, Col still has value 10, and the inner loop does not execute anymore. The proper approach is to make sure that the loop condition variable of the inner loop is initialized inside the outer loop. This initialization correction is shown in EXAMPLE2 below. // EXAMPLE 2 Row = 0; while (Row < 10) { Row++; Col = 0; while (Col < 10) { Col++; cout << Row << ¡¨ , ¡¨ << Col << endl; } cout << endl; } 12.6 Compound Conditions Chapter 11 did have a reason for existing. You are about to use the Boolean logic that was presented there. Many situations in life present compound conditions and we will start by looking at two programs that decide if a person should be hired. The hiring criteria is based on years of education and years of work experience. The first program example uses a logical or to decide if somebody should be hired. In this case somebody is qualified if they have the required education or they have the required work experience. C++ uses two vertical lines ( || ), which are located above the key to indicate the logical or. // PROG1213.CPP // This program demonstrates compound decisions with or ( || ). #include #include void main() { int Education; // years of education int Experience; // years of work experience clrscr(); cout << "ENTER YEARS OF EDUCATION ===>> "; cin >> Education; cout << "ENTER YEARS OF WORK EXPERIENCE ===>> "; cin >> Experience; cout << endl; if ((Education >= 16) || (Experience >= 5)) cout << "YOU ARE HIRED"; else cout << "YOU ARE NOT QUALIFIED"; cout << endl << endl; cout << "HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES"; getch(); } PROG1213.CPP OUTPUT #1 ENTER YEARS OF EDUCATION ===>> 16 ENTER YEARS OF WORK EXPERIENCE ===>> 5 YOU ARE HIRED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1213.CPP OUTPUT #2 ENTER YEARS OF EDUCATION ===>> 16 ENTER YEARS OF WORK EXPERIENCE ===>> 3 YOU ARE HIRED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1213.CPP OUTPUT #3 ENTER YEARS OF EDUCATION ===>> 12 ENTER YEARS OF WORK EXPERIENCE ===>> 5 YOU ARE HIRED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1213.CPP OUTPUT #4 ENTER YEARS OF EDUCATION ===>> 12 ENTER YEARS OF WORK EXPERIENCE ===>> 3 YOU ARE NOT QUALIFIED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES The four output samples of PROG1213.CPP show three hire situations and one ¡§not qualified¡¨ response. These four outputs closely resemble the appearance of the truth tables, which was first introduced with or. The next example, program PROG1214.CPP, is identical but now the compound statement is decided by a logical and. C++ uses two ampersands ( && ) to indicate a logical and. Logical Operators in C++ C++ uses || to indicate a logical or. C++ uses && to indicate a logical and. C++ uses ! to indicate a logical not. The logical and is far more demanding. There are four possible hiring combinations, and just like the truth table, only one will qualify. A person must have the proper education and also the required work experience. // PROG1214.CPP // This program demonstrates compound decisions with and ( && ) #include #include void main() { int Education; // years of education int Experience; // years of work experience clrscr(); cout << "ENTER YEARS OF EDUCATION ===>> "; cin >> Education; cout << "ENTER YEARS OF WORK EXPERIENCE ===>> "; cin >> Experience; cout << endl << endl; if ((Education >= 16) && (Experience >= 5)) cout << "YOU ARE HIRED"; else cout << "YOU ARE NOT QUALIFIED"; cout << endl << endl; cout << "HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES"; getch(); } PROG1214.CPP OUTPUT #1 ENTER YEARS OF EDUCATION ===>> 16 ENTER YEARS OF WORK EXPERIENCE ===>> 5 YOU ARE HIRED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1214.CPP OUTPUT #2 ENTER YEARS OF EDUCATION ===>> 16 ENTER YEARS OF WORK EXPERIENCE ===>> 3 YOU ARE NOT QUALIFIED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1214.CPP OUTPUT #3 ENTER YEARS OF EDUCATION ===>> 12 ENTER YEARS OF WORK EXPERIENCE ===>> 5 YOU ARE NOT QUALIFIED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES PROG1214.CPP OUTPUT #4 ENTER YEARS OF EDUCATION ===>> 12 ENTER YEARS OF WORK EXPERIENCE ===>> 3 YOU ARE NOT QUALIFIED HAVE A NICE DAY FROM XYZ EMPLOYMENT SERVICES 12.7 Program Input Protection Chapter 11 introduced Boolean Algebra laws. This chapter could get really tedious if a program example is shown for each one of the Boolean laws. The focus here will be on one of the most important Boolean laws in introductory computer science, DeMorgan¡¦s Law. Let us start by looking at program PROG1215.CPP, which shows one approach that uses Boolean logic properly. The purpose of the program is to make sure that proper input is entered from the keyboard. This program example could be part of any type of survey or record that asks the program user to enter his or her sex. The program is expected to prevent any entry, except an M or an F. The compound decision first considers what the proper input should be. Then a set of parenthesis is placed around the desired M or F condition. The not decides if the condition is not the desired one. Check the program out on the next page and notice the combination of the or and the not. // PROG1215.CPP // This program demonstrates compound decision with a do...while // loop. The program checks for proper data entry. #include #include void main() { char Sex; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; } while (!((Sex == 'M') || (Sex == 'F'))); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1215.CPP OUTPUT Enter your sex [M/F] ===>> Q Enter your sex [M/F] ===>> A Enter your sex [M/F] ===>> 1 Enter your sex [M/F] ===>> 2 Enter your sex [M/F] ===>> m Enter your sex [M/F] ===>> f Enter your sex [M/F] ===>> F Your sex is F Program PROG1216.CPP is very similar to the previous program. Close, but significantly different. A very important set of parenthesis is left out. The result is that the not only applies to the Sex == ¡¦M¡¦ component of the condition. This results in a logic error. This type of logic error alters the input protection so that only M is accepted as keyboard input. Does it make sense why that is the only correct input? Put on your best logic hat and see if you figure out what is happening. // PROG1216.CPP // This program demonstrates compound decision with a do...while // loop with logic problems due to missing parenthesis. #include #include void main() { char Sex; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; } while (!(Sex == 'M') || (Sex == 'F')); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1216.CPP OUTPUT Enter your sex [M/F] ===>> D Enter your sex [M/F] ===>> Q Enter your sex [M/F] ===>> F Enter your sex [M/F] ===>> M Your sex is M The logical or makes a compound statement true if either one of the two conditions is true. Without the parenthesis the condition (Sex == ¡¦F¡¦) is not controlled by the not. This means that entering F makes the condition true and the loop body is executed. This is not desirable because the loop body asks to enter Sex again. In other words, F will not stop the loop. Entering M does stop the loop. The not does apply to the (Sex == ¡¦M¡¦) condition and the loop works properly for that portion of the overall statement. To make sure that you do understand the difference, the two statements are shown together. The correct statement is shown first followed by the wrong statement. while (!((Sex == 'M') || (Sex == 'F'))); // correct while (!(Sex == 'M') || (Sex == 'F')); // wrong Other compound decision problems can crop up in programs. The next program example uses a totally different approach and checks if the input is not correct for Male or not correct for Female. You may think that the loop is logically correct, and you expect it to provide proper protection against erroneous keyboard input. Check the program output to see if you are right. // PROG1217.CPP // This program demonstrates incorrect use of negative compound // decision structure. #include #include void main() { char Sex; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; } while ((Sex != 'M') || (Sex != 'F')); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1217.CPP OUTPUT Enter your sex [M/F] ===>> D Enter your sex [M/F] ===>> Q Enter your sex [M/F] ===>> F Enter your sex [M/F] ===>> M Enter your sex [M/F] ===>> f Enter your sex [M/F] ===>> m Loop never exits. No value satisfies the compound condition. Something is definitely wrong. The loop does not go anywhere, except right back to where it started. This happens no matter what you enter. The problem is caused by the logical or combined with some negative conditions. You are seeing an example of not using Boolean logic properly. In particular DeMorgan¡¦s law is ignored. Let us look at DeMorgan¡¦s Law again. DeMorgan¡¦s Law not(A or B) is equivalent to not(A) and not(B) not(A and B) is equivalent to not(A) or not(B) Note that removal of the parenthesis is not simply a matter of distributing the not to A and B. The logical operator is altered. Failure to change the operator is a very common mistake and results in the faulty program logic. We showed an earlier program - that worked properly - with the following conditional statement: while (!((Sex == 'M') || (Sex == 'F'))); // correct Then another program was shown with identical logic. The main difference is that the program did not use so many parenthesis. The condition was simplified and probably looked perfectly correct. while ((Sex != 'M') || (Sex != 'F')); // incorrect According to DeMorgan we have just committed a serious nono and the program will not work correctly. Evidence of a logic problem cropped up immediately when we were stuck in an infinite loop. Some people have a hard time seeing this type of logic. You really must use the and operator. Input is wrong if the input is not male and at the same time the input is not female. The problem with the or operator is that not male includes female, just as not female includes male. Using or means that the condition will always be satisfied and the loop goes round and round. It is not possible to find any input that will stop the loop. The next program example alters the logical operator from or to and which outputs good results. Our good friend DeMorgan will be pleased that we have seen, and hopefully understood the error of our ways. Boolean logic shall not be ignored without consequences to the results of your program, not to mention the results to your computer science grades. // PROG1218.CPP // This program demonstrates correct use of negative compound // decision structure using DeMorgan's Law. #include #include void main() { char Sex; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; } while ((Sex != 'M') && (Sex != 'F')); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1218.CPP OUTPUT #1 Enter your sex [M/F] ===>> Q Enter your sex [M/F] ===>> W Enter your sex [M/F] ===>> M Your sex is M PROG1218.CPP OUTPUT #2 Enter your sex [M/F] ===>> F Your sex is F // PROG1219.CPP // This program demonstrates using a boolean data type // with an input protection loop. #include #include #include "BOOL.H" void main() { char Sex; bool Correct; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; Correct = (Sex == 'M') || (Sex == 'F'); if (!Correct) cout << "Incorrect input; please re-enter" << endl; } while (!Correct); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1219.CPP Enter sex [M/F] ===>> Q Incorrect input; please re-enter Enter sex [M/F] ===>> D Incorrect input; please re-enter Enter sex [M/F] ===>> m Incorrect input; please re-enter Enter sex [M/F] ===>> f Incorrect input; please re-enter Enter sex [M/F] ===>> M Your sex is M Using Boolean Data Types with Input Protection The bool data type exists for the purpose of readability. In the area of variety bool is seriously lacking. There are only two values for bool, and that is true and false. Yet with those two small data values we can create programs that are much nicer because they create such excellent readability. Program PROG1220.CPP continues the tired saga of preventing erroneous keyboard input. A little excitement and improvement is added because now lower-case m and f will also work with the program. The main focus of the program is on the Boolean data type. In this case the loop continues while the condition is !Correct. Now that is very readable. Inside the loop there is also a statement that says if (!Correct) and then a message follows. The beauty of the Boolean data type is that we can make our programs closer to English. This is not important the day that you write the program. But next week, next month, and especially next year you will not always remember the meaning of some program code. At such times you will appreciate any extra readability that you added to your program source code. // PROG1220.CPP // This program demonstrates using a boolean data type // with an input protection loop. // It also shows a compound condition to exit the loop. // A user has to enter a PIN and is given only three. #include #include #include "BOOL.H" void main() { char Sex; bool Correct; clrscr(); do { cout << "Enter sex [M/F] ===>> "; cin >> Sex; Correct = (Sex == 'M') || (Sex == 'F') || (Sex == 'm') || (Sex == 'f'); if (!Correct) cout << "Incorrect input; please re-enter" << endl; } while (!Correct); cout << endl; cout << "Your sex is " << Sex << endl; getch(); } PROG1220.CPP OUTPUT #1 Enter sex [M/F] ===>> Q Incorrect input; please re-enter Enter sex [M/F] ===>> q Incorrect input; please re-enter Enter sex [M/F] ===>> Q Incorrect input; please re-enter Enter sex [M/F] ===>> f Your sex is f PROG1220.CPP OUTPUT #2 Enter sex [M/F] ===>> F Your sex is F PROG1220.CPP OUTPUT #3 Enter sex [M/F] ===>> m Your sex is m PROG1220.CPP OUTPUT #4 Enter sex [M/F] ===>> M Your sex is M The next program example shows protection against a variety of potentially erroneous inputs. You have certainly seen enough protection against wrong characters. Program PROG1221.CPP protects against a wrong Password (apstring), a wrong Age (integer), and a wrong GPA (real number). The logic is similar, but note that each loop is specialized for the data type and the conditions required of the particular variable. do . . . while and Input Protection You will see do...while used frequently for input protection loops. The post-condition loop makes sense for checking erroneous input because you want the program to enter the loop at least one time. // PROG1221.CPP // This program demonstrates a variety of input protection // with an input protection loop. // It also shows how to handle upper and lower case input. #include #include #include "BOOL.H" #include "APSTRING.H" apstring Password; int Age; float GPA; void EnterPassword(); void EnterAge(); void EnterGPA(); void DisplayData(); void main() { clrscr(); EnterPassword(); EnterAge(); EnterGPA(); DisplayData(); getch(); } void EnterPassword() { bool CorrectPassword; clrscr(); do { cout << "Enter password ===>> "; cin >> Password; CorrectPassword = Password == "QWERTY"; } while (!CorrectPassword); } void EnterAge() { bool CorrectAge; cout << endl << endl; do { cout << "Enter age ===>> "; cin >> Age; CorrectAge = (Age >= 0) && (Age <= 130); } while (!CorrectAge); } void EnterGPA() { bool CorrectGPA; cout << endl << endl; do { cout << "Enter GPA ===>> "; cin >> GPA; CorrectGPA = (GPA >= 0) && (GPA <= 4); } while (!CorrectGPA); } void DisplayData() { cout << endl << endl; cout << "PASSWORD: " << Password << endl; cout << "AGE: " << Age << endl; cout << "GPA: " << GPA << endl; } PROG1221.CPP OUTPUT Enter password ===>> AARDVARK Enter password ===>> QWERTY Enter age ===>> 150 Enter age ===>> 52 Enter GPA ===>> 7.785 Enter GPA ===>> 3.999 PASSWORD: QWERTY AGE: 52 GPA: 3.999 We will finish the section by looking at a practical situation that occurs in real life. Individuals entering a password to login to a network, or people entering a PIN to use an ATM card should not just be able to enter data forever. Somebody who has stolen an ATM card or somebody who has no business logging in as a different user, should not have unlimited access to trying to enter the system. There have been a variety of movies where clever young kids hook up a computer program to some data base and the computer takes all night methodically trying to crack into the system by trying millions of different passwords. Now this looks cute on television or in a movie, but do give me a break. Anybody who uses a program that sits around waiting for millions of break-in attempts deserves to get trouble. There has to be a limit on input attempts. Our final program in this section shows just such a situation. It is also another nice example of using a compound condition. Program PROG1222.CPP shows part of a program that checks the proper PIN (Personal Identification Number) of an ATM (Automatic Teller Machine) card. The compound condition has been set up to accept three attempts at entering the correct PIN. After three tries it is over. In such a case the machine can also do fun things like keep the card. // PROG1222.CPP // This program shows the need for a practical compound condition // used with an input protection loop. The program request the // user PIN, but rejects access after three tries. #include #include void main() { int PIN; int Tries = 0; clrscr(); do { cout << "Please enter your PIN ===>> "; cin >> PIN; Tries++; } while ((PIN != 8423) && (Tries < 3)); if (PIN == 8423) cout << "Your PIN is correct" << endl; else cout << "You have exceeded your PIN entries" << endl; getch(); } PROG1222.CPP OUTPUT #1 Please enter your PIN ===>> 4325 Please enter your PIN ===>> 4326 Please enter your PIN ===>> 4327 You have exceeded your PIN entries PROG1222.CPP OUTPUT #2 Please enter your PIN ===>> 4325 Please enter your PIN ===>> 4326 Please enter your PIN ===>> 8423 Your PIN is correct 12.8 Short-Circuiting Decisions C++ is pretty clever and takes advantage of something called short circuiting. The name is very appropriate if you think why we use the term short circuit with electricity. Electric current takes a path that travels to some appliance like a freezer, television or a light bulb. The path traveled by the electricity through the wires and the appliance is called a circuit. The appliance will only operate properly is a complete circuit exists. Sometimes, wires may be faulty and insulation is missing that causes a short circuit. Current jumps across the wires and a circuit is completed before the appliance is reached. In such a case a fuse burns or a circuit breaker is switched off to prevent fire damage from the overheated circuits. The concept applies to computer science decisions as well. A situation exists that stops the normal path in favor of a shorter path. Consider the following, rather complicated Boolean expression: A and ( (A or B) and (B or C) and (A or C) or ((B and C) or (A and B))) This expression can be quite an unpleasant truth table. Now suppose that it is known that A equals false, is it necessary to evaluate the entire expression? No, it is not because the and logical operator guarantees that the final outcome will always be false. In this case we can short circuit the evaluation. Similar logic applies with or. If it is given that A equals true then the entire expression will evaluate to true. Once again it is not necessary to consider the rest of the expression. A or ( (A or B) and (B or C) and (A or C) or ((B and C) or (A and B))) Program PROG1223.CPP uses the short circuit principle. If the first condition, N1 % 2 == 0 equals false then it is not necessary to consider the value of the second condition, N2 % 2 == 0. It is entirely possible that you are totally unimpressed by this program. You do appreciate that C++ utilizes short-circuiting, but little evidence is shown to you about the process. // PROG1223.CPP // This program uses "short circuiting" but it is not // noticeable at all. #include #include void main() { clrscr(); int N1,N2; cout << "Enter two positive integers ===>> "; cin >> N1 >> N2; if (N1 % 2 == 0 && N2 % 2 == 0) cout << "Both numbers are even" << endl; else cout << "Both numbers are not even" << endl; getch(); } PROG1223.CPP OUTPUT #1 Enter two positive integers ===>> 12 24 Both numbers are even PROG1223.CPP OUTPUT #2 Enter two positive integers ===>> 11 24 Both numbers are not even PROG1223.CPP OUTPUT #3 Enter two positive integers ===>> 24 11 Both numbers are not even The next two program examples will not only involve compound decisions, but they will also demonstrate that short circuiting is performed by C++. Each program call uses a function, called IsEven, shown below. This function is called from the conditional statement. Calling a function inside a conditional statement is perfectly legal since it is a return function. Return functions return values. You have seen return functions that return integers and real numbers. Function IsEven returns true or false, since it is a Boolean function. The purpose of the function is to return true if the function argument Number is even and false if Number is odd. The function intentionally displays some output so that you can see that the function is called. bool IsEven(int Number) { cout << endl; cout << "Calling IsEven Function" << endl; cout << endl; if (Number % 2 == 0) return true; else return false; } You have not yet learned how to write the type of function that you are viewing above. At this stage this does not matter. You have used plenty of functions without a clue how those functions work. The bottom line is that a function is called that displays a message, letting you know it is called, and the function returns true if the parameter is even. In program PROG1224.CPP two integers are requested from the keyboard, as before. The entered numbers are used in the parameters of the two IsEven function calls. Now C++ claims that it will not consider evaluating a complete compound condition if true or false can be guaranteed without moving on. In other words, if the first condition is false and a logical and is used, then the whole condition will be false. We can test this theory by running the program several times. Each call to function IsEven is noticed because it displays output. When the first number entered is odd, you should only observe one function call. // PROG1224.CPP // This program uses "short circuiting" and uses the IsEven // function to demonstrate short circuiting with logical and. #include #include #include "BOOL.H" bool IsEven(int Number) { cout << endl; cout << "Calling IsEven Function" << endl; cout << endl; if (Number % 2 == 0) return true; else return false; } void main() { clrscr(); int N1,N2; cout << "Enter two positive integers ===>> "; cin >> N1 >> N2; if ( IsEven(N1) && IsEven(N2) ) cout << "Both numbers are even" << endl; else cout << "Both numbers are not even" << endl; getch(); } PROG1224.CPP OUTPUT #1 Enter two positive integers ===>> 12 24 Calling IsEven Function Calling IsEven Function Both numbers are even PROG1224.CPP OUTPUT #2 Enter two positive integers ===>> 11 24 Calling IsEven Function Both numbers are not even PROG1224.CPP OUTPUT #3 Enter two positive integers ===>> 24 11 Calling IsEven Function Calling IsEven Function Both numbers are not even The output of program PROG1224.CPP does not disappoint us. As anticipated, only the first part of the compound condition, (IsEven(N1) && IsEven(N2)) is evaluated when it is established that the whole condition must be false. This is proven by the single call to the IsEven function when the first number entered is an odd number. The next program example is very similar to the previous program, and it uses the same IsEven function. This time short circuiting is tested with a logical or. The concept of short circuiting is the same, but in this case C++ stops evaluating when it is established that the first condition is true. You will find in future chapters that short circuiting becomes a useful tool in preventing incorrect program executions. In particular, short circuiting with the logical and will be used frequently. It is appropriate to introduce the concept in a chapter that introduces compound conditions that are evaluated with Boolean logic laws. Practical use of this concept will need to be postponed to a later chapter. // PROG1225.CPP // This program uses "short circuiting" and uses the IsEven // function to demonstrate short circuiting with logical or. #include #include #include "BOOL.H" bool IsEven(int Number) { cout << endl; cout << "Calling IsEven Function" << endl; cout << endl; if (Number % 2 == 0) return true; else return false; } void main() { clrscr(); int N1,N2; cout << "Enter two positive integers ===>> "; cin >> N1 >> N2; if ( IsEven(N1) || IsEven(N2) ) cout << "One or more numbers are even" << endl; else cout << "Neither numbers are even" << endl; getch(); } PROG1225.CPP OUTPUT #1 Enter two positive integers ===>> 11 23 Calling IsEven Function Calling IsEven Function Neither numbers are even PROG1225.CPP OUTPUT #2 Enter two positive integers ===>> 11 24 Calling IsEven Function Calling IsEven Function One or more numbers are even PROG1225.CPP OUTPUT #3 Enter two positive integers ===>> 24 11 Calling IsEven Function One or more numbers are even 12.9 Recursion, a Sneak Preview The title of this section is very appropriate. The real recursion show comes later. Recursion is a very important topic in computer science and you will be exposed to this topic many, many times. The intention at the end of this chapter is to plant a seed for a future topic. The hope is that the seed will find fertile ground in your brain and maybe become a small plant by the time we return to the topic. So what is recursion anyway, and what is the big deal? The big deal has to wait, but the basic essence of recursion is very appropriate in a chapter on control structures. Is this a recurring theme or what? Every time you learn something, you are told to hang loose for more information in the future. Relax, this is intentional and it is all part of the Exposure scheme of learning. If you received all the information about a particular topic in one concentrated introduction, you would probably be severely overwhelmed and possibly throw in the towel. Recursion Definition Recursion is the process of a function calling itself. This certainly is a meager, skimpy definition. It is very skimpy, but it is very true. A function is executed by calling it. Now we have seen that functions can be called from the main function and functions can be called from other functions. Well, why not call a function from the same function? Weird? Perhaps. Check out program PROG1226.CPP. This program calls a function that counts numbers. Each time that the function is called, the value of X is incremented and displayed. There is one call in the main function to get the ball rolling. After that, function Count takes care of business and happily calls itself. // PROG1226.CPP // This program introduces recursion as an alternative to // repetition. This example creates an infinite loop. #include #include int X = 1000; void Count(); void main() { clrscr(); Count(); getch(); } void Count() { X++; cout << "X = " << X << endl; Count(); } PROG1226.CPP OUTPUT X = 1001 X = 1002 X = 1003 X = 1004 X = 1005 X = 1006 X = 1007 X = 1008 X = 1009 X = 1010 ........ DOES NOT END Your suspicions about this Schram guy are becoming more valid. Now the guy introduces a new computer science concept that cleverly sends a program into an infinite loop. Fine, you now see the purpose for recursion in this chapter. Without using a loop structure you can simulate repetition. Make a function call itself and it goes round and round. You can then watch numbers flash on by the monitor until you get tired. But let us hold on here for a moment. Is it not very easy to create an infinite loop with other control structures? If the loop condition variable does not change properly a loop may never exit. Every loop structure that has been introduced has some kind of condition that determines when the loop is finished. In all fairness we need to give recursion the same courtesy and allow a condition to control its destiny as well. The next program counts once again, but now we feel that counting 15 times is sufficient. The recursive call is only made as long as the condition if (X < 1015) is true. Important Recursion Warning It is easy to write programs with recursive functions that repeat in an infinite pattern of recursive calls. Some special condition must be tested before each recursive call is made. // PROG1227.CPP // This program introduces recursion as an alternative to // repetition. This example solves the infinite recursion // problem with a conditional statement before the recursive call. #include #include int X = 1000; void Count(); void main() { clrscr(); Count(); getch(); } void Count() { X++; cout << "X = " << X << endl; if (X < 1015) Count(); } PROG1227.CPP OUTPUT X = 1001 X = 1002 X = 1003 X = 1004 X = 1005 X = 1006 X = 1007 X = 1008 X = 1009 X = 1010 X = 1011 X = 1012 X = 1013 X = 1014 X = 1015 Please realize that this was a very brief introduction to recursion. There will be several chapters on this topic in the future. Some students like to play around with recursion and see what it does. For now the important idea is that you know an alternative to regular loop structures. Recursion Notes Recursion is the process of a function calling itself. Recursion simulates repetition. Recursion requires some condition that exits the recursive calling to prevent infinite repetition. The special condition that stops the recursive calls is called the base case. You will find in some future topics that there exist a variety of programming problems that are easier to solve with recursive structures than with iterative loop structures. 12.10 Worked-Out Exercises The exercises that follow will be in the same pattern as the exercises presented in Chapter X. Each exercise presents a brief program with the mission to determine the program output. The essence of the exercise revolves around tracing the variable values as they change during program execution. This time the exercises will be more challenging with the addition of nested control structures and compound conditions. Exercise 01 #include #include void main() { clrscr(); for (int X = 1; X < 2; X++) for (int Y = 1; Y < 3; Y++) cout << X << Y << endl; getch(); } X Y Output 1 1 11 1 2 12 Exercise 02 #include #include void main() { clrscr(); for (int X = 1; X < 3; X++) { for (int Y = 1; Y < 4; Y++) cout << X * Y << " "; cout << endl; } getch(); } X Y Output 1 1 1 1 2 1 2 1 3 1 2 3 2 1 2 2 2 2 4 2 3 2 4 6 Exercise 03 #include #include void main() { clrscr(); int X = 5; int K,Y; while (X <= 8) { Y = 1; while (Y <= 7) { Y++; K = X + Y; } X += Y; } cout << "K = " << K << endl; getch(); } Y K X Output 5 1 2 7 3 8 4 9 5 10 6 11 7 12 8 13 13 K = 13 A common mistake with this type of output is to stop when Y equals 7. At that time the inner while loop is still continued since the loop condition checks to see if Y is less than or equal to 7. Failure to execute that last iteration through a loop structure causes an error than is called an OBOB. OBOB Warning An OBOB is an Off By One Bug error. This problem occurs at the boundaries of control structures. Carefully check your program code at the boundaries to avoid OBOBs. Exercise 04 #include #include void main() { clrscr(); int A = 1, B = 2, C = 3, D = 4; while (A < B && C < D) { A = C + D; C = A + B; } cout << A << B << C << D << endl; getch(); } A B C D Output 1 2 3 4 7 9 7294 This example uses a compound condition in the while loop. The trace shows that both conditions become false. Keep in mind that this is not a requirement for a logical and. As long as one of the condition becomes false, the loop will exit. Exercise 05 #include #include void main() { clrscr(); int A; int B = 2; for (int K = 0; K < 4; K++) { A = K; B += K; while (A < B) { cout << A << " " << B << endl; A += B; B += K; } } getch(); } K A B Output 2 0 0 2 0 2 2 2 1 1 3 1 3 4 4 2 2 6 2 6 8 8 3 3 11 3 11 14 14 Exercise 06 #include #include void main() { clrscr(); int A = 0; int B = 10; for (int K = A; K < B; K++) { A++; B--; cout << A << " " << B << " " << K << endl; } getch(); } K A B Output 0 10 0 1 9 1 9 0 1 2 8 2 8 1 2 3 7 3 7 2 3 4 6 4 7 3 4 5 5 5 5 4 5 You need to be careful with this type of exercise. The for loop condition checks to see if K < B and this condition changes. The value of B is not constant for the duration of the loop. Initially the value of B equals 10, but then decrements each time the loop is repeated. If we want to use the for loop as a fixed iteration loop, this style of programming should be discouraged. You may realize that the for loop can be manipulated in such a way that it behaves in the same manner as a while loop. Exercise 07 #include #include void main() { clrscr(); int T = 0; for (int P = 1; P <= 4; P++) for (int Q = 1; Q <= 4; Q++) T++; cout << "T = " << T << endl; getch(); } P Q T Output 0 1 1 1 2 2 3 3 4 4 2 1 5 2 6 3 7 4 8 3 1 9 2 10 3 11 4 12 4 1 13 2 14 3 15 4 16 T = 16 Exercise 08 // EX1208.CPP #include #include void main() { clrscr(); int T = 0; for (int P = 0; P < 10; P++) for (int Q = 1; Q < 10-P; Q++) T++; cout << "T = " << T << endl; getch(); } P Q T Output 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 1 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8 17 2 1 18 2 19 3 20 4 21 5 22 6 23 7 24 3 1 25 2 26 3 27 4 28 5 29 6 30 4 1 31 2 32 3 33 4 34 5 35 5 1 36 2 37 3 38 4 39 6 1 40 2 41 3 42 7 1 43 2 44 8 1 45 T = 45 The trace of the last program is quite tedious. Hopefully, you can see that there is a pattern that makes the output easier to determine. The inner loop iterates 9 times during the first pass, 8 times during the second pass, and once during the last pass. There are 9 passes from 9 down to 1 inner repetitions. This means that the inner loop repeats an average of 5 times. If you multiply 9 times 5 you get 45. Did the shortcut explanation make any sense? To some students it probably made sense right away. To some other students, this may be another story. Keep in mind that you can always use the long trace approach. It may be tedious, but it is accurate and you do not have to play guessing games. The main point is that you get out some paper and pencil and trace through the program either one step at a time, recording each variable value, or you determine some type of pattern that helps you predict the outcome. Exercise 09 #include #include void main() { clrscr(); int N1, N2, N3; N1 = N2 = N3 = 1; for (int K = 3; K <= 10; K++) { N3 = N1 + N2; N1 = N2; N2 = N3; } cout << "N3 = " << N3 << endl; getch(); } K N3 N1 N2 Output 1 1 1 3 2 1 2 4 3 2 3 5 5 3 5 6 8 5 8 7 13 8 13 8 21 13 21 9 34 21 34 10 55 34 55 N3 = 55 The program above computes the Fibonacci Sequence. This is a sequence of integers, such that each integer in the sequence is the sum of the two previous integers. The output in this case is the 10th number in the sequence. Exercise 10 #include #include void main() { clrscr(); int A = 5; int B = 10; int C = 13; while (A < B && B != C) { A += 2; B++; if ((A + B) % 2 == 0) C++; } cout << A << " " << B << " " << C << endl; getch(); } A B C A < B B != C Output 5 10 13 true true 7 11 14 true true 9 12 true true 11 13 15 true true 13 14 true true 15 15 16 false true 15 15 16 Exercise 11 // EX1211.CPP #include #include void main() { clrscr(); int A, B , C; if (A < B) if (B < A) C = 1000; else C = 2500; else if (B > A) C = 2000; else C = 2500; cout << "C = " << C << endl; getch(); } A B C Output ? ? ? C = 2500 This problem may at first appear like it cannot have a solution. A normal trace with changing variable values does not apply. On the other hand, you can apply logic to determine the output of this program. Assume that A < B is true. It is not possible at the same time that B < A is also true. This means that the flow continues to the first "nested else" and C = 2500. Now if A < B is false, execution flows jumps to the "outer else" and checks to see if B > A is true. With A < B false it must also be the case that B > A is false and execution flow continues to the second "nested else" where C = 2500. This means that the values of the variables are not important. Regardless of any starting values the output will always be that C = 2500. Exercise 12 #include #include void main() { clrscr(); for (int X = 1; X <= 4; X++) { for (int Y = 1; Y <= 4; Y++) cout << X + Y << " "; cout << endl; } getch(); } X Y X + Y Output 1 1 2 2 1 2 3 2 3 1 3 4 2 3 4 1 4 5 2 3 4 5 2 1 3 3 2 2 4 3 4 2 3 5 3 4 5 2 4 6 3 4 5 6 3 1 4 4 3 2 5 4 5 3 3 6 4 5 6 3 4 7 4 5 6 7 4 1 5 5 4 2 6 5 6 4 3 7 5 6 7 4 4 8 5 6 7 8 2 3 4 5 3 4 5 6 4 5 6 7 5 6 7 8 The output is shown as each number is added to the display. In reality, that view will not be seen, unless you stop the output. The final completed output is shown at the very end to show the complete display. APCS Examination Alert Computers are not used during an APCS Examination. You will need to determine the correct program execution of program segments and functions throughout the test. The ability to "play computer" and trace systematically through program code in the same manner as program execution is a value skills. For the remainder of the course you will be exposed to many different types of tracing situations. Exercise 08 Consider the following function. void Hello(int &A, int &B) { while (A != B && B >=0) { A++; B--; } } Assume that X and Y are integers. What is the output of the following program segment? X = 3; Y = 6; Hello(X,Y); cout << X << " " << Y; (A) 9 0 (B) 9 -1 (C) 10 0 (D) 10 -1 (E) There is no output. Function Hello never terminates. X = 3; Y = 6; Hello(X,Y); X Y &A (X) &B (Y) 3 6 3 6 4 5 5 4 6 3 7 2 8 1 9 0 10 -1 After Function Call X = 10 Y = -1 Answers is (D) Exercise 09 Consider the following function. void Hello(int &A, int &B) { while (A != B && B >=0) { A++; B--; } } For which values of A and B will function Hello terminate? (A) Function Hello will never terminate. (B) For all integer values of A and B, such that A > B is true (C) For all integer values of A and B, such that A <= B is true (D) For all integer values of A and B, such that A <> B is true (E) For all integer values of A and B Answer is (E) In the compound statement (A != B && B >= 0) the first condition can be ignored. Strictly looking at B >= 0 combined with the logical and, the function will always terminate. If B < 0 the while loop starts out as false and the function will terminate immediately. If B >= 0 then the loop is entered and the statement B-- will guarantee that B eventually becomes negative. APCS Examination Alert There are a large selection of multiple choice questions that involve computing the output or return value. You will also find that some questions ask you to determine some general property of a function, such as Exercise 09 above. Frequently, such questions can be determined by trying a set of test data so that a pattern can be observed. The pattern will usually indicate the general case. Compare this question to the previous exercise that used the same function. Exercise 10 void Ex1410(int &X) { int Y; while (X < 10) { Y = 1; while (Y <= 10) { Y++; X += 2; } X += Y; } } int P = 5; Ex1410(P); P Y X (P) 5 5 1 2 7 3 9 4 11 5 13 6 15 7 17 8 19 9 21 10 23 11 25 36 After Function Call P = 36

P>