CS1440-Lab (5)
More about Functions

The goals is to learn about:
    void functions (functions that do not return anything)
    functions calling other functions
    functions which return more than one value
    complex programs with multiple function calls

Activity 1: Preparation
Create the lab5 directory under the 1440 directory and complete your work there.

Activity 2: Void Functions
We have seen and used functions that return a value. Sometimes, it is convenient to create a function that carries out some activities but does not return a value to the calling function. Such a function may perform some calculations and display the result.  A good example of such a function is one that displays a set of instructions on the screen regarding how a program works.  In program P44.C of the previous lab, we used a function to compute the total cost of purchases.  In that program, not much information or instructions on how the program works was provided.  We can improve it by adding some information that tells the user more about what the program does and what the inputs/outputs are. If we put the extra information in the main function, we will clutter up the main and make it longer and more tedious to read. Instead, we can put the user instructions in a separate function named (what else?) "instructions". This function do not have any argument, it has a name (instruction), and since it does not return any value has a type void. Look at the new version of the program given below and see how the instructions function is being defined and called in the main function.

// P51.CThis program computes the total cost of purchases made.
#include <iostream.h>

double total_cost(int number_par, double price_par);
//Computes the total cost, including 5% sales tax,
//on number_par items at a cost of price_par each.

void instructions(void);  //Tells the user what the program is doing.

int main( )
{
        double price, bill;
        int number;

        instructions();
        cout << "Enter the number of items purchased: ";
        cin >> number;
        cout << "Enter the price per item $";
        cin >> price;

        bill = total_cost(number, price);

        cout.setf(ios::fixed);
        cout.setf(ios::showpoint);
        cout.precision(2);
        cout << number << " items at "
                 << "$" << price << " each.\n"
                 << "Final bill, including tax, is $" << bill
                 << endl;

        return 0;
}

double total_cost(int number_par, double price_par)
{
        const double TAX_RATE = 0.05; //5% sales tax
        double subtotal;

        subtotal = price_par * number_par;
        return (subtotal + subtotal*TAX_RATE);
}

void instructions(void)
{
        cout << "\n\nBILL CALCULATOR!\n\n"
             << "This program will calculate the total cost, "
             << "including tax,\n"
             << "of a quantity of items being purchased.  You "
             << "will be asked\n"
             << "for the number of items purchased and the cost "
             << "of each one.\n\n";
}

As you can see,  the function that displays the instructions has 1) a name, instructions, 2) a type, void, and 3) valid parameter(s), none.  Since this function is of type void, it does need to return anything, if it had the return statement, it would return nothing, thus, return;.

To highlight the difference between a void function and a function that return a value,  for each of the two functions in the above program, we have shown the prototype of functions and the calls to them.

prototype:      double total_cost(int number_par, double price_par);
call:           bill = total_cost(number, price);

prototype:      void instructions(void);
call:           instructions();

The total_cost function returns a value of type double.  This value will be "received" by the calling function with the same type.  Thus, when it comes back to the calling function, we have to save the returned value in a variable of the same type. That's why we put the call on the right-hand-side of an assignment statement and assign the return value to the variable, bill. Also, total_cost has two parameters. The first one is an integer and the second one is a double. When we call the total_cost function, we must pass to it two values, an integer and a double.

Now look at the prototype and the call for the instructions function. The instructions function has a void return type (the word void tells C++ that the function returns no value), so it is not necessary to catch any return value. And since the function has no parameters, it is not necessary to send it any values when we call it. Thus the call includes only the name of the function and an empty argument list. Note that the parentheses after the name of the function are necessary. Also note that the word "void" is not used in the call. Instead, the parameter list is simply left blank. It is an error to put the word "void" in the function call.

Don't let the difference between the names of the parameters in the total_cost function and the names of the variables whose values are being passed to the function bother you. When C++ gets to the assignment statement, "bill = total_cost(number, price)", the first thing it does is to look into memory and get the values of number and price. It then sends those values to the total_cost function. The values are used to initialize the parameters of the total_cost function. The total_cost function has no way of knowing what the variables in main are called, and main has no way of knowing what the parameters of the total_cost function are called. The calling mechanism merely copies values from one place in memory to another. The names of those places may be exactly the same or they may be different; it doesn't matter.  Corresponding parameters in the function call, function prototype, and function definition MUST have the same type.

There is one other void function that we might find nice to have for our cost.C program. The formatting lines

cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);

You can move these lines to a void function so your main program is more readable.

Exercise 51
Edit the program P51.C and move the formatting lines into a void function named "format", call the new program ex51.C and save it under your lab5 directory. In the main, call the "format" function from inside the main by placing format(); in place of all three lines.  Note, the location where you call this function is very important.  You may even call this function from inside the body of the instructions function.   Make sure to have the function prototype for the function format() before the function instructions.  If you call it after you have displayed the results, then it won't have any effect on the format of the output.

Compile and run the program and see the change in the format of the output.

Exercise 52
The format function that you use in ex51.C is not flexible.  In other words, the function will only displays the output with 2 decimal points.  If you need to write your output with a different number of decimal points, let's say 3, then you need to create another format function with a different name, perhaps format3(), and in that function change the precision to 3, cout.precision(3);.  As you may notice, this is not convenient at all.  Is there any other way to do this?  yes.

Edit the void function "format" such that it takes two parameters as its input, the number that you wish to print and the number of decimal points.
void format(double x, int n)

Note that your function is still a void function, but this time it is taking two parameters. Now, you have more flexibility. You can call this function any time you wish to change the precision.   Any number that you send to this function as a parameter will be printed with n decimal point.  Suppose we want to print the cost with 3 decimal points, then we can call the format function as; format(cost, 3).

The good thing about the new function is that you call it as many time as you wish to change the format of the output without changing the name of the function each time.

Compile and run the program and see the change in the format of the output.

Activity 3:Functions that "Return" More than One Value (call_by-reference)
In lab4, you saw examples of functions that returned one value.  In the first example on this lab, you saw a function that didn't return anything.  Sometimes, you want to return more than one value from a function.  The return statement can be used to return one value only.  Thus, we will not use the return statement to return more than one value.  Instead we will use a method to update the parameters that are passed to a function.  Following is an example in which we have used a new method to return a value to the main without using the return statement.

In this example, the get_input function is to obtain two values from the user, then returns (brings) the two values to the main function. In a sense, then, get_input "returns" two values.  This cannot be done with the return statement because the return statement returns exactly one value. If a function must produce more than one output value, then we must use call-by-reference parameters (one for each output value.)

Simple Example (please pay attention to the color):

// P52.C This function is to illustrate how a function can return two values
#include<iostream.h>

// This is the prototype for the function that reads the values for i and j
void get_input(int& i, int& j);

// This is the prototype for the function that add 10 to i and 20 to j
void Process(int& i, int j);

int main()
{
     int i,j;

     get_input(i,j);

     cout << "I am about to call function Process, i = " << i << " j = " << j << endl;

     Process(i,j);

     cout << "I just came back from function Process, i = " << i << " j = " << j << endl;

    return 0;
}

void get_input(int& i, int& j)
{
       cout << "Please enter two values for i and j separated by a single space, then press <Enter>:";
       cin >> i >> j;
       cout << endl;
       return;   // a void function, returns nothing
}

void Process(int& i, int j)
{
      i = i +10;
      j = j +20;
      cout << "Inside function Process \n";
     cout << "I added 10 to i, and 20 to j, i = " << i << " and j = " << j << "\n";
}

Exercise 53
Cut and paste this program into a program called ex53.C, compile then run it.  Which one of the values, i or j, got updated upon return from the function Process?  What would you do to get both values updated upon your return to the main? make the necessary changes and run the program to make it works correctly.

Using & we access the address of a variable,  thus, when we make a change in the value in a function, the change will be seen in the calling function as well, i.e., the value gets updated.  Calling a variable by its address is referred to as call-by-reference.

One more note, if we want to use return statements to get the values for i and j, we needed to have two different functions, one to get the value for i (example: get_i) and another to get the value for j (example: get_j).

Both of these methods work, a programmer should make the program work correctly and efficiently while maintaining simplicity.

Activity 4:A Complex Programs with Multiple Function Calls
Suppose we have been asked to create a program to create rectangles and triangles on the screen. The shapes will be made out of a character chosen by the user. The user may also choose the size of the rectangle or triangle by entering the number of rows and columns for a rectangle and the number of rows for a triangle. Here are some samples:

xxxxx
xxxxx
xxxxx

o
oo
ooo
oooo
ooooo
oooooo

The first shape is a rectangle made of x's with 3 rows and 5 columns. The second shape is a triangle made of o's with 6 rows. All our triangles will be right triangles drawn so that each row after the first row is one character longer than the row just above it.

Your homework for this lab will be to create this program, but I am going to lead you through its creation.

Sometimes when you write a new function, you make use of a driver program to test it. A driver is a small program written only to test a new function. When you are working on a program that will contain many functions, you should test each function separately from the others. Once a function has been tested, it can be used in a program that is testing a different function, but each function should be tested in a program in which it is the only untested function.

We are also going to make use of function stubs. We will say more about this idea shortly.

The first task to be done when writing a large program is to decide on a design for that program. The most important part of designing a program is deciding what functions need to be written and what kind of function each will be.

We can describe a design most easily with a picture called a structure chart. The structure chart contains a rectangle for each function labeled with the name of the function. The rectangles are drawn in such a way that the chart shows which function calls which. If function X calls function Y, then the rectangle for X will be above that for Y and there will be a line connecting X to Y. The following structure chart describes our new and improved cost.C.

For each function we show the inputs to the function with a downward-pointing arrow, and the outputs with an upward-pointing arrow. If there are more than two outputs, as for the get_input function, we will use call-by-reference parameters for those outputs. Otherwise we will use the return statement. The order in which functions are shown left to right is unimportant in a structure chart. The only information conveyed is "who calls who and how". Make sure you understand why the structure chart for cost.C is drawn the way it is.

Activity 5:Design
Here is a structure chart for the draw.C program we are going to write for homework.

In addition to the structure chart in our design, we need to say a few words about what each function does. These explanations can later be made part of the comments written next to the prototypes of the functions. Here is a brief description of each function to be included in draw.C:

instructions: The instructions function will tell the user that the drawing program will draw rectangles and triangles of different sizes on the screen and that the user may select the size and the character to be used for drawing. It should tell the user that he or she will be making selections from a menu.

menu: The menu function will display the following menu:

1. Draw rectangle.
2. Draw triangle.
3. Quit

It will then ask the user to select one of the choices. The function should verify that the choice is a 1, 2, or 3, and should then return that choice to main.

draw_rect: Given a number of rows, a number of columns, and a character to use for drawing, this function draws a rectangle on the screen. It calls
the make_row function to produce each row of the rectangle.

draw_tri: Given a number of rows and a character to use for drawing, this function draws a triangle on the screen. It calls the make_row function to
produce each row of the triangle. Each row of the triangle after the first row is one character longer than the row above it.

make_row: Given a number, num, and a character, ch, this function prints num ch's in a row. It does not place a newline at the end of the row.

get_rows_cols: The get_rows_cols function is called when the user wants to draw a rectangle. The function asks the user for the number of rows and the number of columns to be used in creating the rectangle, and "returns" those numbers to main. (Call-by-reference!)

get_rows: The get_rows function is called when the user wants to draw a triangle. The function asks the user for the number of rows to be used in creating the triangle and returns that number to main.

get_drawchar: The get_drawchar function is called for any shape the user wants to draw. The function asks the user what character should be used in the drawing and returns that character to main.

Activity 6:Implementing the design

You should not change the design of the draw.C program. The design was crafted to be a valuable learning experience for you. This is certainly not the only way that the draw.C program could be written, but it is the way you are expected to write it.

One hard part of the program is now done. We have decided how the work to be done by the program should be divided up among all our functions. Now we pick a function to write first and get started. Normally we begin by writing the main function, but this time let's begin by writing the make_row function.

make_row

The make_row function has two call-by-value parameters, an integer and a character. Let's call the integer num and the character ch. The function is supposed to write ch on the screen num times. We will need a loop that runs num times to accomplish this task. Create a file called "driver.C" and copy the following into it. The main function here is a driver for the make_row function.

#include <iostream.h>

void make_row (int num, char ch);
//Writes num ch's on the screen, all in a row.

int main (void)
{
        int num;
        char ch;

        cout << "\n\nDriver to test make_row function.\n\n";
        cout << "This program will repeatedly ask you for a "
             << "number and a character\n"
             << "and will send that number and character to "
             << "make_row.  When you\n"
             << "are satisfied that make_row works properly, "
             << "enter 0 for the number.";
        cout << "\n\nYour number (0 to quit): ";
        cin >> num;
        while (num > 0)
        {
                cout << "\nYour character: ";
                cin >> ch;
                make_row(num, ch);
                cout << "\n\nYour number (0 to quit): ";
                cin >> num;
        }
        return 0;
}

A driver is a function designed to test another newly written function. The main function above will not be used in the draw.C program. Its usefulness is temporary, only long enough to be sure that the make_row function is correct.

Activity 7:Algorithm
Now write the make_row function below main. Give it a loop that runs num times, printing ch each time. Once you have written the function, compile and test your program. You can't go forward until your make_row function is working properly.

main

Now let's write the main function for the draw.C program. Here is an algorithm that describes what main must do. See if the algorithm makes sense to you:

  * 1.Print instructions for the user.
  * 2.Print the menu and get the user's choice (1, 2, or 3).
  * 3.While the user's choice is not 3
  *        a. If the user's choice is 1 (draw a rectangle)
  *          1.Get the number of rows and the number of columns to use for the rectangle.
  *          2.Get the character to use in drawing the rectangle.
             3.Draw the rectangle.
  *           4.Print the menu again and get the user's next choice.

  *      b. Else if the user's choice is 2 (draw a triangle)
  *           1.Get the number of rows to use for the triangle.
  *           2.Get the character to use in drawing the triangle.
             3.Draw the triangle.
  *            4.Print the menu again and get the user's next choice.

  *     c. Thank the user for using the program.
 

After you think about the order in which we have listed the steps of the algorithm, figure out which instructions of the algorithm go with which function. The complete program should have the functions listed below:

     instructions
     menu
     draw_rect
     draw_tri
     make_row
     get_rows_cols
     get_rows
     get_drawchar

You should now begin to see how the main function will be encoded in C++. Step 1 of the algorithm will be replaced by a call to the instructions function. Steps 2 and 3(c) will be replaced by calls to the menu function. Step 3(a)(i) will be replaced by a call to get_rows_cols, step 3(b)(i) will be replaced by a call to get_rows, and steps 3(a)(ii) and 3(b)(ii) will be replaced by calls to get_drawchar. Finally, step 3(a)(iii) will be replaced by a call to draw_rect and step 3(b)(iii) will be replaced by a call to draw_tri. Notice that there is no call in main to the make_row function we have already written. That's because draw_rect and draw_tri are the callers of that function.

Exercise 54
You need to complete the parts marked with an * and red font in the algorithm section in the previous section. Create a file called draw.C. Begin with the #include line, then write all the function prototypes necessary for this part with comments for each one. You should be able to decide on the form of a prototype by looking at the structure chart and at the description of the function below the structure chart. If the function produces no output value, it is a void function. Otherwise, it's return type will be the return type of its output value, unless, of course, the function produces two output values, like get_rows_cols. Then it will be a void function and it will have two call-by-reference parameters. To decide on the parameter list for each function, look at the structure chart and see if the function has any inputs. If so, it will need a call-by-value parameter for each of its inputs. The only function that needs call-by-reference parameters is get_rows_cols.

After you have written all the prototypes, write the main function using the algorithm given above. You might find it useful to copy and paste the logarithm into your program and then change each step into C++. Many of the steps turn into function calls. Go ahead and write the appropriate function calls. Don't neglect to declare any variables that you use in your main.

We are not going to write every function in final form right now, but we are going to write a stub for each function. A stub is a function that tells you its name, tells you the values of its call-by-value parameters, and returns some reasonable value(s) if it is supposed to. Like a driver, a stub's usefulness is temporary. As your program progresses toward completion, you turn each stub into a finished function, one at a time.

After your main, insert the following:

int get_rows(void)
{
        cout << "Get_rows function called.\n";
        return 5;
}

void get_rows_cols(int& rows, int& cols)
{
        cout << "Get_rows_cols function called.\n";
        rows = 5;
        cols = 10;
}

void instructions(void)
{
        cout << "Instructions function called.\n";
}

int menu(void)
{
        int choice;

        cout << "Menu function called.\n";
        cout << "What shall I return for a choice this time? ";
        cin >> choice;
        return choice;
}

These are 4 of the stubs we need in the program. Be sure that the first line of each stub looks like the prototype you placed above main. The body of a stub should contain a cout statement saying that the function has been called. If the function has call-by-value parameters, the stub should contain cout's that say what the values received were. If the function is supposed to return a value, you should choose a reasonable value and have the stub return that value. The value 5 was selected for get_rows to return because 5 is a reasonable number for rows of a triangle (get_rows is called when the user wants to draw a triangle.)

Study the stubs you just placed in your program. Be sure you understand what each one is going to do when your main calls it.

You might wonder about the menu stub. It presented a little problem. If we made 1 be the return value, then main would always call the draw_rect function. If we made 2 be the return value, then only draw_tri would get called, and if we made 3 be the return value then the program would just quit.  So we will have the menu stub read a value from the user and return that value.

All you want to do right now is test to see if your main function works. Write a stub for each of the other functions called by main and put them underneath main. The order of the stubs is not critical. Whatever order you choose will probably be the order of the functions in the final program. It is usually best to put them in alphabetical order so you can find them easily.

After you have finished all the stubs, compile and run draw.C. It will not, of course, draw any rectangles or triangles, but you should be able to tell if the right functions are getting called at the right times. To complete the program, you will turn each of the stubs into a finished function. Let's start with get_drawchar.

get_drawchar

This function does not receive any arguments. It simply asks the user for a character and returns that character to main. Edit draw.C and find the stub for get_drawchar. You will now delete the cout that says that the function was called and you will replace it with a cout that asks the user to enter a character. Declare a local character variable and read the user's character into that variable. Then return the value of that variable instead of the character your stub was returning.

After you write get_drawchar, save your program and compile it. Run the program. The only change in its behavior should be that it now lets you enter a character and it shows you that character from the draw_rect and draw_tri functions, instead of the one you were using in the stub.

get_rows_cols

Let's build the get_rows_cols function next. This will be the only function we need to write that must use call-by-reference parameters. We must use call-by-reference since the function needs to produce two different values.

We are calling get_rows_cols from main and passing main's two variables, rows and cols, to the function. We want the function to receive the variables themselves, and not the values of the variables. So the prototype of the function is:

void get_rows_cols(int& rows, int& cols);

Right now our stub is assigning 5 to rows and 10 to cols. Change the function so that it asks the user for a value for each of its parameters. Be sure to get rid of the cout statement saying that the function was called.

Now compile and run draw.C. You should be able to draw rectangles of any size you choose and out of any character you choose.

Right now is a good time to think about how you want to make use of the screen as the program runs. There should be white space separating your shapes from things above and below them. All the questions that are asked for one shape should come in a cluster. The menu should be set off from other things once you complete the menu function.

At the end of this lab, you need to create an script file that contains all programs, then submit that typescript file.
% script
% more ex51.C
% more ex52.C
% more ex53.C
% more draw.C

Ctrl-D

% more typescript    (view the typescript before you submit it to make sure the file exists and is not empty).

% ~rt/bin/submit1440_102   lab5 typescript

Post_Lab
Your assignment is to finish the draw.C program.  You will have to work on the draw_rect, draw_tri, instructions, and menu.  Note that the algorithm given for this program is a complete algorithm. We implemented part of it in the lab, so you can use it to complete the program.

Part of wrapping up the program should be the addition of a program header containing your name, your login name, the file name, the date, and a brief description of the program. Your variables should have comments after them stating their purpose. Your code should be written using proper indentation style. Each function prototype should be followed by a comment describing that function.

Your program must be submitted before the next lab.

Call your program draw.C and submit using the submit command:

~rt/bin/submit1440_102  lab5  draw.C