Part IV
Structures and File Input/Output
Structures
Using structures, you have the ability to group data and work with the grouped data as a whole. Business data processing uses the concept of structures in almost every program. Being able to ma- nipulate several variables as a single group makes your programs easier to manage.
This chapter introduces the following concepts:
-
Structure definitions
-
Initializing structures
-
The dot operator (. )
-
Structure assignment
-
Nested structures
This chapter is one of the last in the book to present new concepts. The remainder of the book builds on the structure con- cepts you learn in this chapter.
Introduction to Structure s
Structures can have members of different data types.
A structure is a collection of one or more variable types. As you know, each element in an array must be the same data type, and you must refer to the entire array by its name. Each element (called a member) in a structure can be a different data type.
Suppose you wanted to keep track of your CD music collection. You might want to track the following pieces of information about each CD:
Title Artist
Number of songs Cost
Date purchased
There would be five members in this CD structure.
After deciding on the members, you must decide what data type each member is. The title and artist are character arrays, the number of songs is an integer, the cost is floating-point, and the date is another character array. This information is represented like this:
Member Name Data Type
Title Character array of 25 characters
Artist Character array of 20 characters
Number of songs Integer
Cost Floating-point
Date purchased Character array of eight characters
A structure tag is a label for the structure’s format.
Each structure you define can have an associated structure name called a structure tag. Structure tags are not required in most cases, but it is generally best to define one for each structure in your program. The structure tag is not a variable name. Unlike array names, which reference the array as variables, a structure tag is simply a label for the structure’s format.
You name structure tags yourself, using the same naming rules for variables. If you give the CD structure a structure tag named
cd_col l ect i on , you are informing C++ that the tag called cd_col l ect i on
looks like two character arrays, followed by an integer, a floating- point value, and a final character array.
A structure tag is actually a newly defined data type that you, the programmer, define. When you want to store an integer, you do not have to define to C++ what an integer is. C++ already recognizes an integer. When you want to store a CD collection’s data, however, C++ is not capable of recognizing what format your CD collection takes. You have to tell C++ (using the example being described here) that you need a new data type. That data type will be your structure tag, called cd_coll ect i on in this example, and it looks like the struc- ture previously described (two character arrays, integer, floating- point, and character array).
Figure 28.1 shows the CD structure, graphically representing the data types in the structure. Notice that there are five members and each member is a different data type. The entire structure is called cd_coll ect i on because that is the structure tag.
Figure 28.1. The layout of the cd_coll ect i on structure.
Examples
- Suppose
you were asked to write a program for a company’s inventory system. The company had been using a card-file inventory system to track the following items:
Item name Quantity in stock Quantity on order Retail price Wholesale price
This would be a perfect use for a structure containing five members. Before defining the structure, you have to deter- mine the data types of each member. After asking questions about the range of data (you must know the largest item name, and the highest possible quantity that would appear on order to ensure your data types can hold the data), you decide to use the following structure tag and data types:
Member Data Type
Item name Character array of 20 characters
Quantity in stock l ong i nt
Quantity on order l ong i nt
Retail price doubl e
Wholesale price doubl e
- Suppose
the same company also wanted you to write a program to keep track of their monthly and annual salaries and to print a report at the end of the year that showed each month’s individual salary and the total salary at the end of the year.
What would the structure look like? Be careful! This type of data probably does not need a structure. Because all the monthly salaries must be the same data type, a floating- point or a double floating-point array holds the monthly salaries nicely without the complexity of a structure.
Structures are useful for keeping track of data that must be grouped, such as inventory data, a customer’s name and address data, or an employee data file.
Defining Structure s
To define a structure, you must use the st r uct statement. The st r uct statement defines a new data type, with more than one member, for your program. The format of the st r uct statement is
st r uct [ st r uct ur e t ag ]
{
member def i ni t i on ;
member def i ni t i on ;
:
member def i ni t i on ;
} [ one or mor e st r uct ur e var i abl es ];
As mentioned earlier, st r uct ur e t ag is optional (hence the brackets in the format). Each member def i ni t i on is a normal variable definition, such as i nt i ; or f l oat sal es[ 20]; or any other valid variable definition, including variable pointers if the structure re- quires a pointer as a member. At the end of the structure’s definition, before the final semicolon, you can specify one or more structure variables.
If you specify a structure variable, you request C++ to reserve space for that variable. This enables C++ to recognize that the variable is not integer, character, or any other internal data type. C++ also recognizes that the variable must be a type that looks like the structure. It might seem strange that the members do not reserve storage, but they don’t. The structure variables do. This becomes clear in the examples that follow.
Here is the way you declare the CD structure:
st r uct cd_coll ect i on
{
char t i t l e[ 25]; char ar t i st[ 20]; i nt num_songs; f l oat pr i ce;
char dat e_pur ch[ 9];
} cd1, cd2, cd3;
Before going any further, you should be able to answer the following questions about this structure:
-
What is the structure tag?
-
How many members are there?
-
What are the member data types?
-
What are the member names?
-
How many structure variables are there?
-
What are their names?
The structure tag is called cd_coll ect i on . There are five mem- bers, two character arrays, an integer, a floating-point, and a charac- ter array. The member names are t i t l e , ar t i st , num_songs , pr i ce , and dat e_pur ch . There are three structure variables—cd1 , cd2 , and cd3 .
Figure 28.2. Using a card-file CD inventory system.
If you had 1000 CDs, you would have to declare 1000 structure variables. Obviously, you would not want to list that many structure variables at the end of a structure definition. To help define struc- tures for a large number of occurrences, you must define an array of structures. Chapter 29, “Arrays of Structures,” shows you how to do that. For now, concentrate on familiarizing yourself with structure definitions.
Examples
- Here
is a structure definition of the inventory application described earlier in this chapter.
st r uct i nvent or y
{
char i t em_name[ 20]; l ong i nt i n_st ock;
l ong i nt or der _qt y; f l oat r et ail ;
f l oat whol esal e;
} i t em1, i t em2, i t em3, i t em4;
Four inventory structure variables are defined. Each struc- ture variable—i t em1 , i t em2 , i t em3 , and i t em4 —looks like the structure.
- Suppose
a company wanted to track its customers and personnel. The following two structure definitions would create five structure variables for each structure. This ex- ample, having five employees and five customers, is very limited, but it shows how structures can be defined.
st r uct empl oyees
{
char emp_name[ 25] ; / / Empl oyee’ s f ull name.
char addr ess[ 30] ; / / Empl oyee’ s addr ess. char ci t y[ 10];
char st at e[ 2]; l ong i nt zi p;
doubl e sal ar y; / / Annual sal ar y.
} emp1, emp2, emp3, emp4, emp5;
st r uct cust omer s
{
char cust _name[ 25] ; / / Cust omer ’ s f ull name.
char addr ess[ 30] ; / / Cust omer ’ s addr ess. char ci t y[ 10];
char st at e[ 2]; l ong i nt zi p;
doubl e bal ance; / / Bal ance owed t o company.
} cust 1, cust 2, cust 3, cust 4, cust 5;
Each structure has similar data. Later in this chapter, you learn how to consolidate similar member definitions by creating nested structures.
Initializing Structure Dat a
You can define a There are two ways to initialize members of a structure. You
structure’s data when you declare the structure.
can initialize members when you declare a structure, and you can initialize a structure in the body of the program. Most programs lend themselves to the latter method, because you do not always know structure data when you write your program.
Here is an example of a structure declared and initialized at the same time:
st r uct cd_coll ect i on
{
char t i t l e[ 25]; char ar t i st[ 20]; i nt num_songs; f l oat pr i ce;
char dat e_pur ch[ 9];
} cd1 = {“ Red Moon Men” , “ Sam and t he Sneeds” , 12, 11. 95, “ 02/ 13/ 92”} ;
When first learning about structures, you might be tempted to initialize members individually inside the structure, such as
char ar t i st[ 20] =” Sam and t he Sneeds” ; / / I nvali d
You cannot initialize individual members because they are not variables. You can assign only values to variables. The only struc- ture variable in this structure is cd1 . The braces must enclose the data you initialize in the structure variables, just as they enclose data when you initialize arrays.
This method of initializing structure variables becomes tedious when there are several structure variables (as there usually are). Putting the data in several variables, each set of data enclosed in braces, becomes messy and takes too much space in your code.
Use the dot operator to initialize members of structures.
More importantly, you usually do not even know the contents of the structure variables. Generally, the user enters data to be stored in structures, or you read them from a disk file.
A better approach to initializing structures is to use the dot operator (. ). The dot operator is one way to initialize individual members of a structure variable in the body of your program. With the dot operator, you can treat each structure member almost as if it were a regular nonstructure variable.
The format of the dot operator is
st ruct ure_var i abl e_name . member _name
A structure variable name must always precede the dot opera- tor, and a member name must always appear after the dot operator. Using the dot operator is easy, as the following examples show.
Examples
- Here
is a simple program using the CD collection structure and the dot operator to initialize the structure. Notice the program treats members as if they were regular variables when combined with the dot operator.
Identify the program and include the necessary header file. Define a CD structure variable with five members. Fill the CD structure variable with data, then print it.
/ / Fil ename: C28ST1. CPP
/ / St r uct ur e i ni t i ali zat i on wi t h t he CD coll ect i on. #i ncl ude <i ost r eam. h>
#i ncl ude <st r i ng. h> voi d mai n()
{
st r uct cd_coll ect i on
{
char t i t l e[ 25]; char ar t i st[ 20]; i nt num_songs; f l oat pr i ce;
char dat e_pur ch[ 9];
} cd1;
/ / I ni t i ali ze member s her e.
st r cpy( cd1.t i t l e, “ Red Moon Men”) ;
st r cpy( cd1. ar t i st , “ Sam and t he Sneeds”) ; cd1. num_songs=12;
cd1. pr i ce=11. 95;
st r cpy( cd1. dat e_pur ch, “ 02/ 13/ 92”) ;
/ / Pr i nt t he dat a t o t he scr een.
cout << “ Her e i s t he CD i nf ormat i on:\ n\ n” ; cout << “ Ti t l e: “ << cd1.t i t l e << “ \ n” ; cout << “ Ar t i st : “ << cd1. ar t i st << “ \ n” ;
cout << “ Songs: “ << cd1. num_songs << “ \ n” ; cout << “ Pr i ce: “ << cd1. pr i ce << “ \ n” ;
cout << “ Dat e pur chased: “ << cd1. dat e_pur ch << “ \ n” ;
r et ur n;
}
Here is the output from this program:
Her e i s t he CD i nf ormat i on:
Ti t l e: Red Moon Men
Ar t i st : Sam and t he Sneeds Songs: 12
Pr i ce: 11. 95
Dat e pur chased: 02/ 13/ 92
- By using the dot operator, you can receive structure data from the
keyboard with any of the data-input functions you know, such as ci n , get s() , and get .
The following program asks the user for student informa- tion. To keep the example reasonably short, only two stu- dents are defined in the program.
/ / Fil ename: C28ST2. CPP
/ / St r uct ur e i nput wi t h st udent dat a. #i ncl ude <i ost r eam. h>
#i ncl ude <st r i ng. h> #i ncl ude <i omani p. h> #i ncl ude <st di o. h>
voi d mai n()
{
st r uct st udent s
{
char name[ 25]; i nt age;
f l oat aver age;
} st udent 1, st udent 2;
/ / Get dat a f or t wo st udent s.
cout << “ What i s f i r st st udent ’ s name? “ ; get s( st udent 1. name) ;
cout << “ What i s t he f i r st st udent ’ s age? “ ; ci n >> st udent 1. age;
cout << “ What i s t he f i r st st udent ’ s aver age? “ ; ci n >> st udent 1. aver age;
ff l ush( st di n) ; / / Cl ear i nput buff er f or next i nput. cout << “ \ nWhat i s second st udent ’ s name? “ ;
get s( st udent 2. name) ;
cout << “ What i s t he second st udent ’ s age? “ ; ci n >> st udent 2. age;
cout << “ What i s t he second st udent ’ s aver age? “ ; ci n >> st udent 2. aver age;
/ / Pr i nt t he dat a.
cout << “ \ n\ nHer e i s t he st udent i nf ormat i on you “ << “ ent er ed:\ n\ n” ;
cout << “ St udent #1:\ n” ;
cout << “ Name: “ << st udent 1. name << “ \ n” ; cout << “ Age: “ << st udent 1. age << “ \ n” ;
cout << “ Aver age: “ << set pr eci si on( 2) << st udent 1. aver age
<< “ \ n” ;
cout << “ \ nSt udent #2:\ n” ;
cout << “ Name: “ << st udent 2. name << “ \ n” ; cout << “ Age: “ << st udent 2. age << “ \ n” ; cout << “ Aver age: “ << st udent 2. aver age << “ \ n” ;
r et ur n;
}
Here is the output from this program:
What i s f i r st st udent ’ s name? Larr y What i s t he f i r st st udent ’ s age? 14
What i s t he f i r st st udent ’ s aver age? 87. 67
What i s second st udent ’ s name? Judy What i s t he second st udent ’ s age? 15
What i s t he second st udent ’ s aver age? 95. 38
Her e i s t he st udent i nf ormat i on you ent er ed:
St udent #1:
Name: Larr y
Age: 14
Aver age: 87. 67
St udent #2:
Name: Judy
Age: 15
Aver age: 95. 38
- Structure
variables are passed by copy, not by address as arrays are. Therefore, if you fill a structure in a function, you must return it to the calling function in order for the calling function to recognize the structure, or use global structure variables, which is generally not recommended.
Define structures globally and structure variables locally.
The structure tag plays an important role in the local/ global problem. Use the structure tag to define local structure variables. The following program is similar to the previous one. Notice the student structure is defined globally with no
structure variables. In each function, local structure variables are declared by referring to the structure tag. The structure tag keeps you from having to redefine the structure mem- bers every time you define a new structure variable.
/ / Fil ename: C28ST3. CPP
/ / St r uct ur e i nput wi t h st udent dat a passed t o f unct i ons. #i ncl ude <i ost r eam. h>
#i ncl ude <st r i ng. h> #i ncl ude <st di o. h>
#i ncl ude <i omani p. h>
st r uct st udent s f ill _st r uct s( st r uct st udent s st udent _var) ; voi d pr _st udent s( st r uct st udent s st udent _var) ;
st r uct st udent s / / A gl obal st r uct ur e.
{
char name[ 25]; i nt age;
f l oat aver age;
} ; / / No memor y r eser ved.
voi d mai n()
{
st udent s st udent 1, st udent 2; / / Def i nes t wo
/ / l ocal var i abl es.
/ / Call f unct i on t o f ill st r uct ur e var i abl es.
st udent 1 = f ill _st r uct s( st udent 1) ; / / st udent 1
/ / i s passed by copy, so i t must be
/ / r et ur ned f or mai n() t o r ecogni ze i t. st udent 2 = f ill _st r uct s( st udent 2) ;
/ / Pr i nt t he dat a.
cout << “ \ n\ nHer e i s t he st udent i nf ormat i on you” ; cout << “ ent er ed:\ n\ n” ;
pr _st udent s( st udent 1) ; / / Pr i nt s f i r st st udent ’ s dat a. pr _st udent s( st udent 2) ; / / Pr i nt s second st udent ’ s dat a.
r et ur n;
}
st r uct st udent s f ill _st r uct s( st r uct st udent s st udent _var)
{
/ / Get st udent ’ s dat a
ff l ush( st di n) ; / / Cl ear s i nput buff er f or next i nput. cout << “ What i s st udent ’ s name? “ ;
get s( st udent _var . name) ;
cout << “ What i s t he st udent ’ s age? “ ; ci n >> st udent _var . age;
cout << “ What i s t he st udent ’ s aver age? “ ; ci n >> st udent _var . aver age;
r et ur n ( st udent _var) ;
}
voi d pr _st udent s( st r uct st udent s st udent _var)
{
cout << “ Name: “ << st udent _var . name << “ \
n” ; cout << “ Age: “ << st udent _var . age << “
\ n” ; cout << “ Aver age: “ << set pr eci si on( 2)
<<
st udent _var . aver age << “ \ n” ;
r et ur n;
}
The prototype and definition of the f ill _st r uct s() function might seem complicated, but it follows the same pattern you have seen throughout this book. Before a function name, you must declare voi d or put the return data type if the function returns a value. f ill _st r uct s() does return a value, and the type of value it returns is st r uct st udent s .
- Because structure data is nothing more than regular vari- ables
grouped together, feel free to calculate using structure members. As long as you use the dot operator, you can treat structure members just as you would other variables.
The following example asks for a customer’s balance and uses a discount rate, included in the customer’s structure, to calculate a new balance. To keep the example short, the structure’s data is initialized at variable declaration time.
This program does not actually require structures because only one customer is used. Individual variables could have
been used, but they don’t illustrate the concept of calculating with structures.
/ / Fil ename: C28CUST. CPP
/ / Updat es a cust omer bal ance i n a st r uct ur e. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
st r uct cust omer _r ec
{
char cust _name[ 25]; doubl e bal ance;
f l oat di s_r at e;
} ;
voi d mai n()
{
st r uct cust omer _r ec cust omer = {“ St eve Thompson” ,
431. 23, . 25} ;
cout << “ Bef or e t he updat e, “ << cust omer . cust _name; cout << “ has a bal ance of $” << set pr eci si on( 2) <<
cust omer . bal ance << “ \ n” ;
/ / Updat e t he bal ance
cust omer . bal ance *= ( 1. 0- cust omer . di s_r at e) ;
cout << “ Aft er t he updat e, “ << cust omer . cust _name;
cout << “ has a bal ance of $” << cust omer . bal ance << “ \ n” ; r et ur n;
}
- You can copy the members of one structure variable to the members of
another as long as both structures have the same format. Some older versions of C++ require you to copy each member individually when you want to copy one structure variable to another, but AT&T C++ makes duplicating structure variables easy.
Being able to copy one structure variable to another will seem more meaningful when you read Chapter 29, “Arrays of Structures.”
The following program declares three structure variables, but initializes only the first one with data. The other two are then initialized by assigning the first structure variable to them.
/ / Fil ename: C28STCPY. CPP
/ / Demonst r at es assi gni ng one st r uct ur e t o anot her . #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
st r uct st udent
{
char st _name[ 25]; char gr ade;
i nt age;
f l oat aver age;
} ;
voi d mai n()
{
st udent st d1 = {“ Joe Br own” , ‘ A’ , 13, 91. 4} ;
st r uct st udent st d2, st d3; / / Not i ni t i ali zed
st d2 = st d1; / / Copi es each member of st d1
st d3 = st d1; / / t o st d2 and st d3.
cout << “ The cont ent s of st d2:\ n” ;
cout << st d2. st _name << “ “ << st d2. gr ade << “ “ ;
cout << st d2. age << “ “ << set pr eci si on( 1) << st d2. aver age
<< “ \ n\ n” ;
cout << “ The cont ent s of st d3:\ n” ;
cout << st d3. st _name << “ “ << st d3. gr ade << “ “ ; cout << st d3. age << “ “ << st d3. aver age << “ \ n” ;
r et ur n;
}
Here is the output from the program:
The cont ent s of st d2 Joe Br own, A, 13, 91. 4
The cont ent s of st d3 Joe Br own, A, 13, 91. 4
Notice each member of st d1 was assigned to st d2 and st d3
with two single assignments.
Nested Structure s
C++ gives you the ability to nest one structure definition in another. This saves time when you are writing programs that use similar structures. You have to define the common members only once in their own structure and then use that structure as a member in another structure.
The following two structure definitions illustrate this point:
st r uct empl oyees
{
char emp_name[ 25] ; / / Empl oyee’ s f ull name.
char addr ess[ 30] ; / / Empl oyee’ s addr ess. char ci t y[ 10];
char st at e[ 2]; l ong i nt zi p;
doubl e sal ar y; / / Annual sal ar y.
} ;
st r uct cust omer s
{
char cust _name[ 25] ; / / Cust omer ’ s f ull name. char addr ess[ 30] ; / / Cust omer ’ s addr ess. char ci t y[ 10];
char st at e[ 2]; l ong i nt zi p;
doubl e bal ance; / / Bal ance owed t o company.
} ;
These structures hold different data. One structure is for em- ployee data and the other holds customer data. Even though the data should be kept separate (you don’t want to send a customer a paycheck!), the structure definitions have much overlap and can be consolidated by creating a third structure.
Suppose you created the following structure:
st r uct addr ess_i nf o
{
char addr ess[ 30] ; / / Common addr ess i nf ormat i on. char ci t y[ 10];
char st at e[ 2]; l ong i nt zi p;
} ;
This structure could then be used as a member in the other
structures like this:
st r uct empl oyees
{
char emp_name[ 25] ; / / Empl oyee’ s f ull name. addr ess_i nf o e_addr ess; / / Empl oyee’ s addr ess. doubl e sal ar y; / / Annual sal ar y.
} ;
st r uct cust omer s
{
char cust _name[ 25] ; / / Cust omer ’ s f ull name. addr ess_i nf o c_addr ess; / / Cust omer ’ s addr ess.
doubl e bal ance; / / Bal ance owed t o company.
} ;
It is important to realize there are a total of three structures, and
that they have the tags addr ess_i nf o , empl oyees , and cust omer s . How many members does the empl oyees structure have? If you answered three, you are correct. There are three members in both empl oyees and cust omer s . The empl oyees structure has the structure of a character array, followed by the addr ess_i nf o structure, followed by the double floating-point member, sal ar y .
Figure 28.3 shows how these structures look.
Figure 28.3. Defining a nested structure.
When you define a structure, that structure becomes a new data type in the program and can be used anywhere a data type (such as i nt , f l oat , and so on) can appear.
You can assign members values using the dot operator. To assign the customer balance a number, type something like this:
cust omer . bal ance = 5643. 24;
The nested structure might seem to pose a problem. How can you assign a value to one of the nested members? By using the dot operator, you must nest the dot operator just as you nest the structure definitions. You would assign a value to the customer’s ZIP code like this:
cust omer . c_addr ess. zi p = 34312;
this:
To assign a value to the employee’s ZIP code, you would do
empl oyee. e_addr ess. zi p = 59823;
Review Question s
The answers to the review questions are in Appendix B.
-
What
is the difference between structures and arrays?
-
What are the individual elements of a structure called?
-
What are the two ways to initialize members of a structure?
-
Do
you pass structures by copy or by address?
-
True or false: The following structure definition reserves storage
in memory:
st r uct cr ec
{ char name[ 25]; i nt age;
f l oat sal es[ 5]; l ong i nt num;
}
-
Should you declare a structure globally or locally?
-
Should you declare a structure variable globally or locally?
-
How many members does the following structure declara- tion contain?
st r uct i t em
{
i nt quant i t y;
par t _r ec i t em_desc; f l oat pr i ce;
char dat e_pur ch[ 8];
} ;
Review Exercise s
-
Write
a structure in a program that tracks a video store’s tape inventory. Be sure the structure includes the tape title, the length of the tape (in minutes), the initial purchase price of the tape, the rental price of the tape, and the date of the movie’s release.
-
Write
a program using the structure you declared in Exer- cise 1. Define three structure variables and initialize them when you declare the variables with data. Print the data to the screen.
-
Write
a teacher’s program to keep track of 10 students’ names, ages, letter grades, and IQs. Use 10 different struc- ture variable names and retrieve the data for the students in a f or loop from the keyboard. Print the data on the printer when the teacher finishes entering the information for all the students.
Summary
With structures, you have the ability to group data in more flexible ways than with arrays. Your structures can contain mem- bers of different data types. You can initialize the structures either at declaration time or during the program with the dot operator.
Structures become even more powerful when you declare arrays of structure variables. Chapter 29, “Arrays of Structures,” shows you how to declare several structure variables without giving them each a different name. This enables you to step through structures much quicker with loop constructs.
Variable Scop e
The concept of variable scope is most important when you write functions. Variable scope determines which functions recognize certain variables. If a function recognizes a variable, the variable is visible to that function. Variable scope protects variables in one function from other functions that might overwrite them. If a function doesn’t need access to a variable, that function shouldn’t be able to see or change the variable. In other words, the variable should not be “visible” to that particular function.
This chapter introduces you to
-
Global and local variables
-
Passing arguments
-
Automatic and static variables
-
Passing parameters
The previous chapter introduced the concept of using a differ- ent function for each task. This concept is much more useful when you learn about local and global variable scope.
Global Versus Loca l Variables
If you have programmed only in BASIC, the concept of local and global variables might be new to you. In many interpreted versions of BASIC, all variables are global, meaning the entire program knows each variable and has the capability to change any of them. If you use a variable called SALES at the top of the program, even the last line in the program can use SALES. (If you don’t know BASIC, don’t despair—there will be one less habit you have to break!)
Global variables can be dangerous. Parts of a program can inadvertently change a variable that shouldn’t be changed. For example, suppose you are writing a program that keeps track of a grocery store’s inventory. You might keep track of sales percent- ages, discounts, retail prices, wholesale prices, produce prices, dairy prices, delivered prices, price changes, sales tax percentages, holiday markups, post-holiday markdowns, and so on.
The huge number of prices in such a system is confusing. When writing a program to keep track of every price, it would be easy to mistakenly call both the dairy prices d_pr i ces and the delivered prices d_pr i ces . Either C++ will not enable you to do this (you can’t define the same variable twice) or you will overwrite a value used for something else. Whatever happens, keeping track of all these different—but similarly named—prices makes this program con- fusing to write.
Global variables are visible across many program functions.
Local variables are visible only in the block where they are defined.
Global variables can be dangerous because code can inadvert-
ently overwrite a variable initialized elsewhere in the program. It is better to make every variable local in your programs. Then, only functions that should be able to change the variables can do so.
Local variables can be seen (and changed) only from the function in which they are defined. Therefore, if a function defines a variable as local, that variable’s scope is protected. The variable cannot be used, changed, or erased by any other function without special programming that you learn about shortly.
If you use only one function, mai n() , the concept of local and global is academic. You know from Chapter 16, “Writing C++ Functions,” however, that single-function programs are not recom- mended. It is best to write modular, structured programs made up
of many smaller functions. Therefore, you should know how to define variables as local to only those functions that use them.
Defining Variable Scop e
When you first learned about variables in Chapter 4, “Variables and Literals,” you learned you can define variables in two places:
-
Before they are used inside a function
-
Before a function name, such as mai n()
All examples in this book have declared variables with the first method. You have yet to see an example of the second method. Because most these programs have consisted entirely of a single mai n() function, there has been no reason to differentiate the two methods. It is only after you start using several functions in one program that these two variable definition methods become critical.
The following rules, specific to local and global variables, are important:
-
A variable is local if and only if you define it after the opening
brace of a block, usually at the top of a function.
-
A variable is global if and only if you define it outside a
function.
All variables you have seen so far have been local. They have all been defined immediately after the opening braces of mai n() . There- fore, they have been local to mai n() , and only mai n() can use them. Other functions have no idea these variables even exist because they belong to mai n() only. When the function (or block) ends, all its local variables are destroyed.
Global variables are visible from their definition through the remainder of the program.
Global variables are visible (“known”) from their point of definition to the end of the program. If you define a global variable, any line throughout the rest of the program—no matter how many functions and code lines follow it—is able to use that global variable.
Examples
- The
following section of code defines two local variables,
i and j .
mai n( )
{
i nt i , j ; / / Local because t hey’ r e
/ / def i ned aft er t he br ace.
/ / Rest of mai n() goes her e.
}
These variables are visible to mai n() and not to any other function that might follow or be called by mai n() .
- The following section of code defines two global variables, g
and h.
#i ncl ude <i ost r eam. h>
i nt g, h; / / Gl obal because t hey’ r e
/ / def i ned bef or e a f unct i on.
mai n( )
{
/ / mai n() ’ s code goes her e.
}
It doesn’t matter whether your #i ncl ude lines go before or after global variable declarations.
- Global variables can appear before any function. In the following
program, mai n() uses no variables. However, both of the two functions after mai n() can use sal es and pr of i t because these variables are global.
/ / Fil ename: C17GLO. CPP
/ / Pr ogr am t hat cont ai ns t wo gl obal var i abl es. #i ncl ude <i ost r eam. h>
do_f un( ) ;
t hi r d_f un() ; / / Pr ot ot ype di scussed l at er . mai n( )
{
cout << “ No var i abl es def i ned i n mai n() \ n\ n” ;
do_f un() ; / / Call t he f i r st f unct i on.
r et ur n 0;
}
f l oat sal es, pr of i t ; / / Two gl obal var i abl es. do_f un( )
{
sal es = 20000. 00; / / Thi s var i abl e i s vi si bl e
/ / f r om t hi s poi nt down. pr of i t = 5000. 00; / / As i s t hi s one. They ar e
/ / bot h gl obal .
cout << “ The sal es i n t he second f unct i on ar e “ << sal es << “ \ n” ;
cout << “ The pr of i t i n t he second f unct i on i s “ << pr of i t << “ \ n\ n” ;
t hi r d_f un() ; / / Call t he t hi r d f unct i on t o
/ / show t hat gl obal s ar e vi si bl e.
r et ur n 0;
}
t hi rd_f un( )
{
cout << “ I n t he t hi r d f unct i on: \ n” ;
cout << “ The sal es i n t he t hi r d f unct i on ar e “ << sal es << “ \ n” ;
cout << “ The pr of i t i n t he t hi r d f unct i on i s “ << pr of i t << “ \ n” ;
/ / I f sal es and pr of i t wer e l ocal , t hey woul d not be
/ / vi si bl e by mor e t han one f unct i on. r et ur n 0;
}
Notice that the mai n() function can never use sal es and pr of i t because they are not visible to mai n() — even though they are global. Remember, global variables are visible only from their point of definition downward in the program. State- ments that appear before global variable definitions can- not use those variables. Here is the result of running this program.
No var i abl es def i ned i n mai n()
The sal es i n t he second f unct i on ar e 20000 The pr of i t i n t he second f unct i on i s 5000
I n t he t hi r d f unct i on:
The sal es i n t he t hi r d f unct i on ar e 20000 The pr of i t i n t he t hi r d f unct i on i s 5000
- The following program uses both local and global variables. It
should now be obvious to you that j and p are local and i and z are global.
/ / Fil ename: C17GLLO. CPP
/ / Pr ogr am wi t h bot h l ocal and gl obal var i abl es.
/ / Local Var i abl es Gl obal Var i abl es
/ / j , p i , z
#i ncl ude <i ost r eam. h>
pr _agai n() ; / / Pr ot ot ype
i nt i = 0; / / Gl obal var i abl e because i t ’ s
/ / def i ned out si de mai n() .
mai n( )
{
f l oat p ; / / Local t o mai n() onl y.
p = 9. 0; / / Put s val ue i n gl obal var i abl e. cout << i << “ , “ << p << “ \ n” ; / / Pr i nt s gl obal i
/ / and l ocal p.
pr _agai n() ; / / Call s next f unct i on.
r et ur n 0; / / Ret ur ns t o DOS.
}
f l oat z = 9. 0; / / Gl obal var i abl e because i t ’ s
/ / def i ned bef or e a f unct i on.
pr _agai n( )
{
i nt j = 5; / / Local t o onl y pr _agai n() . cout << j << “ , “ << z; / / Thi s can’ t pr i nt p!. cout << “ , “ << i << “ \ n” ;
r et ur n 0; / / Ret ur n t o mai n() .
}
Even though j is defined in a function that mai n() calls, mai n() cannot use j because j is local to pr _agai n() . When pr _agai n() finishes, j is no longer defined. The variable z is global from its point of definition down. This is why mai n() cannot print z. Also, the function pr _agai n() cannot print p because p is local to mai n() only.
Make sure you can recognize local and global variables before you continue. A little study here makes the rest of this chapter easy to understand.
- Two variables can have the same name, as long as they are local to
two different functions. They are distinct variables, even though they are named identically.
The following short program uses two variables, both named age. They have two different values, and they are considered to be two different variables. The first age is local to mai n() , and the second age is local to get _age() .
/ / Fil ename: C17LOC1. CPP
/ / Two di ff er ent l ocal var i abl es wi t h t he same name. #i ncl ude <i ost r eam. h>
get _age() ; / / Pr ot ot ype mai n( )
{
i nt age;
cout << “ What i s your age? “ ; ci n >> age;
get _age() ; / / Call t he second f unct i on. cout << “ mai n() ’ s age i s st ill “ << age << “ \ n” ;
r et ur n 0;
}
get _age( )
{
Variables local to mai n() cannot be used in another function that
mai n() calls.
i nt age; / / A di ff er ent age. Thi s one
/ / i s l ocal t o get _age() . cout << “ What i s your age agai n? “ ;
ci n >> age; r et ur n 0;
}
The output of this program follows. Study this output carefully. Notice that mai n() ’s last cout does not print the newly changed age. Rather, it prints the age known to
mai n() —the age that is local to mai n() . Even though they are named the same, mai n() ’s age has nothing to do with
get _age() ’s age. They might as well have two different vari- able names.
What i s your age? 28
What i s your age agai n? 56 mai n() ’ s age i s st ill 28
You should be careful when naming variables. Having two variables with the same name is misleading. It would be easy to become confused while changing this program later. If these variables truly have to be separate, name them differently, such as ol d_age and new_age, or ag1 and ag2 . This helps you remember that they are different.
- There
are a few times when overlapping local variable names does not add confusion, but be careful about overdo- ing it. Programmers often use the same variable name as the counter variable in a f or loop. For example, the two local variables in the following program have the same name.
/ / Fil ename: C17LOC2. CPP
/ / Usi ng t wo l ocal var i abl es wi t h t he same name
/ / as count i ng var i abl es. #i ncl ude <i ost r eam. h> do_f un() ; / / Pr ot ot ype mai n( )
{
i nt ct r ; / / Loop count er .
f or ( ct r =0; ct r <=10; ct r ++)
{ cout << “ mai n() ’ s ct r i s “ << ct r << “ \
n” ; }
do_f un() ; / / Call second f unct i on.
r et ur n 0;
}
do_f un( )
{
i nt ct r ;
f or ( ct r =10; ct r >=0; ct r--)
{ cout << “ do_f un() ’ s ct r i s “ << ct r << “ \ n” ; }
r et ur n 0; / / Ret ur n t o mai n() .
}
Although this is a nonsense program that simply prints 0
through 10 and then prints 10 through 0, it shows that using
ct r for both function names is not a problem. These variables do not hold important data that must be processed; rather, they are f or loop-counting variables. Calling them both ct r leads to little confusion because their use is limited to con- trolling f or loops. Because a f or loop initializes and incre- ments variables, the one function never relies on the other function’s ct r to do anything.
- Be careful about creating local variables with the same name in the
same function. If you define a local variable early in a function and then define another local variable with the same name inside a new block, C++ uses only the innermost variable, until its block ends.
The following example helps clarify this confusing problem. The program contains one function with three local vari- ables. See if you can find these three variables.
/ / Fil ename: C17MULI. CPP
/ / Pr ogr am wi t h mul t i pl e l ocal var i abl es call ed i . #i ncl ude <i ost r eam. h>
mai n( )
{
i nt i ; / / Out er i
i = 10;
{ i nt i ; / / New bl ock’ s i
i = 20; / / Out er i st ill hol ds a 10. cout << i << “ “ << i << “ \ n” ; / / Pr i nt s 20 20.
{ i nt i ; / / Anot her new bl ock and l ocal var i abl e.
i = 30; / / I nnermost i onl y.
cout << i << “ “ << i <<
“ “ << i << “ \ n” ; / / Pr i nt s 30 30 30.
} / / I nnermost i i s now gone f or ever .
} / / Second i i s gone f or ever ( i t s bl ock ended) . cout << i << “ “ << i << “ “ <<
i << “ \ n” ; / / Pr i nt s 10 10 10.
r et ur n 0;
} / / mai n() ends and so do i t s var i abl es.
All local variables are local to the block in which they are defined. This program has three blocks, each one nested within another. Because you can define local variables immediately after an opening brace of a block, there are three distinct i variables in this program.
The local i disappears completely when its block ends (when the closing brace is reached). C++ always prints the variable that it interprets as the most local—the one that resides within the innermost block.
Use Global Variables Sparingl y
You might be asking yourself, “Why do I have to understand global and local variables?” At this point, that is an understandable
question, especially if you have been programming mostly in BASIC. Here is the bottom line: Global variables can be dangerous. Code can inadvertently overwrite a variable that was initialized in another place in the program. It is better to have every variable in your program be local to the function that has to access it.
Read the last sentence again. Even though you now know how to make variables global, you should avoid doing so! Try to never use another global variable. It might seem easier to use global variables when you write programs having more than one function: If you make every variable used by every function global, you never have to worry whether one is visible or not to any given function. On the other hand, a function can accidentally change a global variable when that was not your intention. If you keep variables local only to functions that need them, you protect their values, and you also keep your programs fully modular.
The Need for Passing Variable s
You just learned the difference between local and global vari- ables. You saw that by making your variables local, you protect their values because the function that sees the variable is the only one that can modify it.
What do you do, however, if you have a local variable you want to use in two or more functions? In other words, you might need a variable to be both added from the keyboard in one function and printed in another function. If the variable is local only to the first function, how can the second one access it?
You have two solutions if more than one function has to share a variable. One, you can declare the variable globally. This is not a good idea because you want only those two functions to have access to the variable, but all functions have access to it when it’s global. The other alternative—and the better one by far—is to pass the local variable from one function to another. This has a big advantage: The variable is only known to those two functions. The rest of the program still has no access to it.
You pass an argument when you pass one local variable to another function.
If a function name has empty parentheses, nothing is being passed to it.
When you pass a local variable from one function to another, you pass an argument from the first function to the next. You can pass more than one argument (variable) at a time, if you want several local variables to be sent from one function to another. The receiving function receives a parameter (variable) from the function that sends it. You shouldn’t worry too much about what you call them— either arguments or parameters. The important thing to remember is that you are sending local variables from one function to another.
A little more terminology is needed before you see some examples. When a function passes an argument, it is called the calling function. The function that receives the argument (called a parameter when it is received) is called the receiving function. Figure
17.1 explains these terms.
Figure 17.1. The calling and receiving functions.
To pass a local variable from one function to another, you must place the local variable in parentheses in both the calling func- tion and the receiving function. For example, the local and global
examples presented earlier did not pass local variables from mai n() to do_f un() . If a function name has empty parentheses, nothing is being passed to it. Given this, the following line passes two vari- ables, t ot al and di scount , to a function called do_f un() .
do_f un( t ot al , di scount ) ;
It is sometimes said that a variable or function is defined. This has nothing to do with the #def i ne preprocessor directive, which defines literals. You define variables with statements such as the following:
i nt i , j ;
i nt m=9;
f l oat x;
char ar a[ ] = “ Tul sa” ;
These statements tell the program that you need these variables to be reserved. A function is defined when the C++ compiler reads the first statement in the function that describes the name and when it reads any variables that might have been passed to that function as well. Never follow a function definition with a semicolon, but always follow the statement that calls a function with a semicolon.
The following program contains two function definitions,
mai n() and pr _i t () .
To practice passing a variable to a function, declare i as an integer variable and make it equal to five. The passing (or calling) function is mai n() , and the receiving function is pr _i t () . Pass the i variable to the pr _i t () function, then go back to mai n() .
mai n() / / The mai n() f unct i on def i ni t i on.
{
i nt i =5; / / Def i nes an i nt eger var i abl e.
pr _i t ( i ) ; / / Call s t he pr _i t () .
/ / f unct i on and passes i t i .
r et ur n 0; / / Ret ur ns t o t he oper at i ng syst em.
}
pr _i t ( i nt i ) / / The pr _i t () f unct i on def i ni t i on.
{
cout << i << “ \ n” ; / / Call s t he cout oper at or . r et ur n 0; / / Ret ur ns t o mai n() .
}
Because a passed parameter is treated like a local variable in the
receiving function, the cout in pr _i t () prints a 5, even though the
mai n() function initialized this variable.
When you pass arguments to a function, the receiving function is not aware of the data types of the incoming variables. Therefore, you must include each parameter’s data type in front of the parameter’s name. In the previous example, the definition of pr _i t () (the first line of the function) contains the type, i nt , of the incoming variable i . Notice that the mai n() calling function does not have to indicate the variable type. In this example, mai n() already knows the type of variable i (an integer); only pr _i t () has to know that i is an integer.
Examples
- Here
is a mai n() function that contains three local variables. mai n() passes one of these variables to the first function and two of them to the second function.
/ / Fil ename: C17LOC3. CPP
/ / Pass t hr ee l ocal var i abl es t o f unct i ons. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
pr _i ni t ( char i ni t i al ) ; / / Pr ot ot ypes di scussed l at er . pr _ot her( i nt age, f l oat sal ar y) ;
mai n( )
{
char i ni t i al ; / / Thr ee var i abl es l ocal t o
/ / mai n() .
i nt age;
f l oat sal ar y;
/ / Fill t hese var i abl es i n mai n() . cout << “ What i s your i ni t i al ? “ ; ci n >> i ni t i al ;
cout << “ What i s your age? “ ; ci n >> age;
cout << “ What i s your sal ar y? “ ; ci n >> sal ar y;
pr _i ni t ( i ni t i al ) ; / / Call pr _i ni t () and
/ / pass i t i ni t i al .
pr _ot her( age, sal ar y) ; / / Call pr _ot her() and
/ / pass i t age and sal ar y.
r et ur n 0;
}
pr _i ni t ( char i ni t i al ) / / Never put a semi col on i n
/ / t he f unct i on def i ni t i on.
{
cout << “ Your i ni t i al i s “ << i ni t i al << “ \ n” ;
r et ur n 0; / / Ret ur n t o mai n() .
}
pr _ot her( i nt age, f l oat sal ar y) / / Must t ype bot h par amet er s.
{
cout << “ You l ook young f or “ << age << “ \ n” ; cout << “ And “ << set pr eci si on( 2) << sal ar y <<
“ i s a LOT of money! ” ;
r et ur n 0; / / Ret ur n t o mai n() .
}
- A receiving function can contain its own local variables.
As long as the names are not the same, these local variables do not conflict with the passed ones. In the following pro- gram, the second function receives a passed variable from mai n() and defines its own local variable called pr i ce_per .
/ / Fil ename: C17LOC4. CPP
/ / Second f unct i on has i t s own l ocal var i abl e. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
comput e_sal e( i nt gall ons) ; / / Pr ot ot ypes di scussed l at er .
mai n( )
{
i nt gall ons;
cout << “ Ri char d’ s Pai nt Ser vi ce \ n” ;
cout << “ How many gall ons of pai nt di d you buy? “ ; ci n >> gall ons; / / Get gall ons i n mai n() .
comput e_sal e( gall ons) ; / / Comput e t ot al i n f unct i on. r et ur n 0;
}
comput e_sal e( i nt gall ons)
{
f l oat pr i ce_per = 12. 45; / / Local t o comput e_sal e() .
cout << “ The t ot al i s “ << set pr eci si on( 2) << ( pr i ce_per * ( f l oat ) gall ons) << “ \ n” ;
/ / Had t o t ype cast gall ons because i t was i nt eger .
r et ur n 0; / / Ret ur n t o mai n() .
}
- The following sample code lines test your skill at recog- nizing
calling functions and receiving functions. Being able to recognize the difference is half the battle of understanding them.
do_i t ( )
The preceding fragment must be the first line of a new function because it does not end with a semicolon.
do_i t 2( sal es) ;
This line calls a function called do_i t 2() . The calling function passes the variable called sal es to do_i t 2() .
pr _i t ( f l oat t ot al )
The preceding line is the first line of a function that receives a floating-point variable from another function that called it. All receiving functions must specify the type of each variable being passed.
pr _t hem( f l oat t ot al , i nt number)
This is the first line of a function that receives two vari- ables— one is a floating-point variable and the other is an integer. This line cannot be calling the function pr _t hem because there is no semicolon at the end of the line.
Automatic Versus Stati c Variables
The terms automatic and static describe what happens to local variables when a function returns to the calling procedure. By default, all local variables are automatic, meaning that they are erased when their function ends. You can designate a variable as automatic by prefixing its definition with the term aut o . The aut o keyword is optional with local variables because they are automatic be default.
The two statements after mai n() ’s opening brace declare auto- matic local variables:
mai n( )
{
i nt i ;
aut o f l oat x;
/ / Rest of mai n() goes her e.
Because aut o is the default, you did not have to include the term
aut o with x.
Automatic variables are local and disappear when their function ends.
The opposite of an automatic variable is a static variable. All global variables are static and, as mentioned, all static variables retain their values. Therefore, if a local variable is static, it too retains its value when its function ends—in case the function is called a second time. To declare a variable as static, place the st at i c keyword in front of the variable when you define it. The following code section defines three variables, i , j , and k. The variable i is automatic, but j and k are static.
my_f un() / / St ar t of new f unct i on def i ni t i on.
{
If local variables are static, their values remain in case the function is called again.
i nt i ;
st at i c j =25; / / Bot h j and k ar e st at i c var i abl es. st at i c k=30;
Always assign an initial value to a static variable when you declare it, as shown here in the last two lines. This initial value is placed in the static variable only the first time my_f un() executes. If you don’t assign a static variable an initial value, C++ initializes it to zero.
Examples
- Consider
this program:
/ / Fil ename: C17STA1. CPP
/ / Tr i es t o use a st at i c var i abl e
/ / wi t hout a st at i c decl ar at i on. #i ncl ude <i ost r eam. h>
t r i pl e_i t ( i nt ct r) ;
mai n( )
{
i nt ct r ; / / Used i n t he f or l oop t o
/ / call a f unct i on 25 t i mes.
f or ( ct r =1; ct r <=25; ct r ++)
{ t r i pl e_i t ( ct r) ; } / / Pass ct r t o a f unct i on
/ / call ed t r i pl e_i t () .
r et ur n 0;
}
t r i pl e_i t ( i nt ct r)
{
i nt t ot al =0, ans; / / Local aut omat i c var i abl es.
/ / Tr i pl es what ever val ue i s passed t o i t
/ / and adds t he t ot al .
ans = ct r * 3; / / Tr i pl e number passed. t ot al += ans; / / Add t r i pl e number s as t hi s i s call ed.
cout << “ The number “ << ct r << “ mul t i pli ed by 3 i s “
<< ans << “ \ n” ;
i f ( t ot al > 300)
{ cout << “ The t ot al of t r i pl e number s i s over 300 \
n” ; } r et ur n 0;
}
This is a nonsense program that doesn’t do much, yet you might sense something is wrong. The program passes num- bers from 1 to 25 to the function called t r i pl e_i t . The function triples the number and prints it.
The variable called t ot al is initially set to 0. The idea here is to add each tripled number and print a message when the total is larger than 300. However, the cout never executes. For each of the 25 times that this subroutine is called, t ot al is reset to 0. The t ot al variable is an automatic variable, with its value erased and initialized every time its procedure is called. The next example corrects this.
- If
you want t ot al to retain its value after the procedure ends, you must make it static. Because local variables are automatic by default, you have to include the st at i c keyword to over- ride this default. Then the value of the t ot al variable is retained each time the subroutine is called.
The following corrects the mistake in the previous program.
/ / Fil ename: C17STA2. CPP
/ / Uses a st at i c var i abl e wi t h t he st at i c decl ar at i on. #i ncl ude <i ost r eam. h>
t r i pl e_i t ( i nt ct r) ; mai n( )
{
i nt ct r ; / / Used i n t he f or l oop t o
/ / call a f unct i on 25 t i mes.
f or ( ct r =1; ct r <=25; ct r ++)
{ t r i pl e_i t ( ct r) ; } / / Pass ct r t o a f unct i on
/ / call ed t r i pl e_i t () .
r et ur n 0;
}
t r i pl e_i t ( i nt ct r)
{
st at i c i nt t ot al =0; / / Local and st at i c
i nt ans; / / Local and aut omat i c
/ / t ot al i s set t o 0 onl y t he f i r st t i me t hi s
/ / f unct i on i s call ed.
/ / Tr i pl es what ever val ue i s passed t o i t and adds
/ / t he t ot al .
ans = ct r * 3; / / Tr i pl e number passed. t ot al += ans; / / Add t r i pl e number s as t hi s i s call ed.
cout << “ The number “ << ct r << “ mul t i pli ed by 3 i s “
<< ans << “ \ n” ;
i f ( t ot al > 300)
{ cout << “ The t ot al of t r i pl e number s i s over 300 \
n” ; } r et ur n 0;
}
This program’s output follows. Notice that the function’s cout is triggered, even though t ot al is a local variable. Be- cause t ot al is static, its value is not erased when the function finishes. When mai n() calls the function a second time, t ot al ’s previous value (at the time you left the routine) is still there.
The number 1 mul t i pli ed by 3 i s 3 The number 2 mul t i pli ed by 3 i s 6 The number 3 mul t i pli ed by 3 i s 9 The number 4 mul t i pli ed by 3 i s 12
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
3
This does not mean that local static variables become global. The main program cannot refer, use, print, or change t ot al because it is local to the second function. Static simply means that the local variable’s value is still there if the program calls the function again.
Three Issues of Paramete r Passing
To have a complete understanding of programs with several functions, you have to learn three additional concepts:
-
Passing arguments (variables) by value (also called “by copy”)
-
Passing arguments (variables) by address (also called “by
reference”)
-
Returning values from functions
The first two concepts deal with the way local variables are passed and received. The third concept describes how receiving functions send values back to the calling functions. Chapter 18, “Passing Values,” concludes this discussion by explaining these three methods for passing parameters and returning values.
Review Question s
The answers to the review questions are in Appendix B.
-
True
or false: A function should always include a r et ur n statement as its last command, even though r et ur n is not required.
-
When
a local variable is passed, is it called an argument or a parameter?
-
True
or false: A function that is passed variables from an- other function cannot also have its own local variables.
-
What must appear inside the receiving function’s parenthe- ses,
other than the variables passed to it?
-
If a function keeps track of a total or count every time it is
called, should the counting or totaling variable be automatic or static?
-
When
would you pass a global variable to a function? (Be careful—this might be a trick question!)
-
How many arguments are there in the following statement?
pr i ntf (“ The r ai n has f all en %d i nches. ” , r ai nf ) ;
Review Exercise s
-
Write
a program that asks, in mai n() , for the age of the user’s dog. Write a second function called peopl e() that computes the dog’s age in human years (by multiplying the dog’s age by seven).
-
Write
a function that counts the number of times it is called. Name the function count _i t () . Do not pass it anything. In the body of count _i t () , print the following message:
The number of t i mes t hi s f unct i on has been call ed i s: ##
where ## is the number. (Hint: Because the variable must be local, make it static and initialize it to zero when you first define it.)
- The
following program contains several problems. Some of these problems produce errors. One problem is not an error, but a bad location for a variable declaration. (Hint: Find all the global variables.) See if you can spot some of the prob- lems, and rewrite the program so it works better.
/ / Fil ename: C17BAD. CPP
/ / Pr ogr am wi t h bad uses of var i abl e decl ar at i ons. #i ncl ude <i ost r eam. h>
#def i ne NUM 10
do_var _f un() ; / / Pr ot ot ypes di scussed l at er .
char ci t y[ ] = “ Mi ami ” ; i nt count;
mai n( )
{
i nt abc;
count = NUM; abc = 5; do_var _f un() ;
cout << abc << “ “ << count << “ “ << pgm_var << “ “
<< xyz; r et ur n 0;
}
i nt pgm_var = 7; do_var_f un( )
{
char xyz = ‘ A’ ;
xyz = ‘ b’ ;
cout << xyz << “ “ << pgm_var << “ “ abc << “ “ << ci t y; r et ur n 0;
}
Summary
Parameter passing is necessary because local variables are better than global. Local variables are protected in their own rou- tines, but sometimes they must be shared with other routines. If local data are to remain in those variables (in case the function is called again in the same program), the variables should be static because otherwise their automatic values disappear.
Most the information in this chapter becomes more obvious as you use functions in your own programs. Chapter 18, “Passing Values,” covers the actual passing of parameters in more detail and shows you two different ways to pass them.
Passing V alue s
C++ passes variables between functions using two different meth- ods. The one you use depends on how you want the passed variables to be changed. This chapter explores these two methods. The con- cepts discussed here are not new to the C++ language. Other programming languages, such as Pascal, FORTRAN, and QBasic, pass parameters using similar techniques. A computer language must have the capability to pass information between functions before it can be called truly structured.
This chapter introduces you to the following:
-
Passing variables by value
-
Passing arrays by address
-
Passing nonarrays by address
Pay close attention because most of the programs in the remain- der of the book rely on the methods described in this chapter.
Passing by Value (by Copy )
The two wordings “passing by value” and “passing by copy” mean the same thing in computer terms. Some textbooks and C++ programmers state that arguments are passed by value, and some state that they are passed by copy. Both of these phrases describe one
When you pass by value, a copy of the variable’s value is passed to the receiving function.
of the two methods by which arguments are passed to receiving functions. (The other method is called “by address,” or “by refer- ence.” This method is covered later in the chapter.)
When an argument (local variable) is passed by value, a copy of the variable’s value is sent to—and is assigned to—the receiving function’s parameter. If more than one variable is passed by value, a copy of each of their values is sent to—and is assigned to—the receiving function’s parameters.
Figure 18.1 shows the passing by copy in action. The value of i — not the variable—is passed to the called function, which receives it as a variable i . There are two variables called i , not one. The first is local to mai n() , and the second is local to pr _i t () . They both have the same names, but because they are local to their respective functions, there is no conflict. The variable does not have to be called i in both functions, and because the value of i is sent to the receiving function, it does not matter what the receiving function calls the variable that receives this value.
Figure 18.1. Passing the variable i by value.
In this case, when passing and receiving variables between functions, it is wisest to retain the same names. Even though they are not the same variables, they hold the same value. In this example, the value 5 is passed from mai n() ’s i to pr _i t () ’s i .
Because a copy of i ’s value (and not the variable itself) is passed to the receiving function, if pr _i t () changed i , it would be changing only its copy of i and not mai n() ’s i . This fact truly separates functions and variables. You now have the technique for passing a copy of a variable to a receiving function, with the receiving function being unable to modify the calling function’s variable.
All C++’s nonarray variables you have seen so far are passed by value. You do not have to do anything special to pass variables by value, except to pass them in the calling function’s argument list and receive them in the receiving function’s parameter list.
Examples
- The
following program asks users for their weight. It then passes that weight to a function that calculates the equiva- lent weight on the moon. Notice the second function uses the passed value, and calculates with it. After wei ght is passed to the second function, that function can treat wei ght as though it were a local variable.
Identify the program and include the necessary input/output file.
You want to calculate the user’s weight on the moon. Because you have to hold the user’s weight somewhere, declare the variable
wei ght as an integer. You also need a function that does the calculations, so create a function called moon() .
Ask the user how much he or she weighs. Put the user’s answer in wei ght . Now pass the user’s weight to the moon() function, which divides the weight by six to determine the equivalent weight on the moon. Display the user’s weight on the moon.
You have finished, so leave the moon() function, then leave the
mai n() function.
/ / Fil ename: C18PASS1. CPP
/ / Cal cul at e t he user ’ s wei ght i n a second f unct i on. #i ncl ude <i ost r eam. h>
moon( i nt wei ght ) ; / / Pr ot ot ypes di scussed l at er .
mai n( )
{
i nt wei ght ; / / mai n() ’ s l ocal wei ght. cout << “ How many pounds do you wei gh? “ ;
ci n >> wei ght;
moon( wei ght ) ; / / Call t he moon() f unct i on and
/ / pass i t t he wei ght.
r et ur n 0; / / Ret ur n t o t he oper at i ng syst em.
}
moon( i nt wei ght ) / / Decl ar e t he passed par amet er .
{
/ / Moon wei ght s ar e 1/ 6t h ear t h’ s wei ght s
wei ght / = 6; / / Di vi de t he wei ght by si x.
cout << “ You wei gh onl y “ << wei ght << “ pounds on t he moon! ” ;
r et ur n 0; / / Ret ur n t o mai n() .
}
The output of this program follows:
How many pounds do you wei gh? 120 You wei gh onl y 20 pounds on t he moon!
- You
can rename passed variables in the receiving function. They are distinct from the passing function’s variable. The following is the same program as in Example 1, except the receiving function calls the passed variable ear t h_wei ght . A new variable, called moon_wei ght , is local to the called func- tion and is used for the moon’s equivalent weight.
/ / Fil ename: C18PASS2. CPP
/ / Cal cul at e t he user ’ s wei ght i n a second f unct i on. #i ncl ude <i ost r eam. h>
moon( i nt ear t h_wei ght ) ; mai n( )
{
i nt wei ght ; / / mai n() ’ s l ocal wei ght. cout << “ How many pounds do you wei gh? “ ;
ci n >> wei ght;
moon( wei ght ) ; / / Call t he moon() f unct i on and
/ / pass i t t he wei ght.
r et ur n 0; / / Ret ur n t o t he oper at i ng syst em.
}
moon( i nt ear t h_wei ght ) / / Decl ar e t he passed par amet er .
{
i nt moon_wei ght ; / / Local t o t hi s f unct i on.
/ / Moon' s wei ght s ar e 1/ 6t h of ear t h’ s wei ght s.
moon_wei ght = ear t h_wei ght / 6; / / Di vi de wei ght by si x.
cout << “ You onl y wei gh “ << moon_wei ght << “ pounds on t he moon! ” ;
r et ur n 0; / / Ret ur n t o mai n() .
}
The resulting output is identical to that of the previous program. Renaming the passed variable changes nothing.
- The
next example passes three variables—of three different types—to the called function. In the receiving function’s parameter list, each of these variable types must be declared.
This program prompts users for three values in the mai n() function. The mai n() function then passes these variables to the receiving function, which calculates and prints values related to those passed variables. When the called function modifies a variable passed to the function, notice again that this does not affect the calling function’s variable. When variables are passed by value, the value—not the variable— is passed.
/ / Fil ename: C18PASS3. CPP
/ / Get gr ade i nf ormat i on f or a st udent. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
check_gr ade( char l gr ade, f l oat aver age, i nt t est s) ;
mai n( )
{
char l gr ade; / / Lett er gr ade.
i nt t est s; / / Number of t est s not yet t aken.
f l oat aver age; / / St udent ’ s aver age based on 4. 0 scal e.
cout << “ What l ett er gr ade do you want ? “ ; ci n >> l gr ade;
cout << “ What i s your curr ent t est aver age? “ ; ci n >> aver age;
cout << “ How many t est s do you have l eft ? “ ; ci n >> t est s;
check_gr ade( l gr ade, aver age, t est s) ; / / Call s f unct i on
/ / and passes t hr ee var i abl es by val ue.
r et ur n 0;
}
check_gr ade( char l gr ade, f l oat aver age, i nt t est s)
{
swi t ch ( t est s)
{
case ( 0) : { cout << “ You will get your curr ent gr ade “
<< “ of “ << l gr ade; br eak; }
case ( 1) : { cout << “ You st ill have t i me t o br i ng “ <<
“ up your aver age” ;
cout << “ of “ << set pr eci si on( 1) << aver age << “ up. St udy har d! ” ;
br eak; }
def aul t : { cout << “ Rel ax. You st ill have pl ent y of “
<< “ t i me. ” ;
br eak; }
}
r et ur n 0;
}
When you pass by
Passing by Addres s (by Reference )
The two phrases “by address” and “by reference” mean the
address, the address of the variable is passed to the receiving function.
same thing. The previous section described passing arguments by value (or by copy). This section teaches you how to pass arguments by address.
When you pass an argument (local variable) by address, the variable’s address is sent to—and is assigned to—the receiving function’s parameter. (If you pass more than one variable by ad- dress, each of their addresses is sent to—and is assigned to—the receiving function’s parameters.)
Variable Addresse s
All variables in memory (RAM) are stored at memory ad- dresses—see Figure 18.2. If you want more information on the internal representation of memory, refer to Appendix A, “Memory Addressing, Binary, and Hexadecimal Review.”
Figure 18.2.
When you tell C++ to define a variable (such as i nt i ; ), you are requesting C++ to find an unused place in memory and assign that place (or memory address) to i . When your program uses the variable called i , C++ goes to i ’s address and uses whatever is there.
If you define five variables as follows,
i nt i ;
f l oat x=9. 8;
char ar a[ 2] = { ‘ A’ , ‘ B’ } ; i nt j =8, k=3;
C++ might arbitrarily place them in memory at the addresses shown in Figure 18.3.
Figure 18.3. Storing variables in memory.
You don’t know what is contained in the variable called i because you haven’t put anything in it yet. Before you use i , you should initialize it with a value. (All variables—except character variables—usually use more than 1 byte of memory.)
Sample Progra m
All C++ arrays are The address of the variable, not its value, is copied to the
passed by address.
receiving function when you pass a variable by address. In C++, all arrays are automatically passed by address. (Actually, a copy of their address is passed, but you will understand this better when you learn more about arrays and pointers.) The following important rule holds true for programs that pass by address:
Every time you pass a variable by address, if the receiving function changes the variable, it is changed also in the calling function.
Therefore, if you pass an array to a function and the function changes the array, those changes are still with the array when it returns to the calling function. Unlike passing by value, passing by address gives you the ability to change a variable in the called function and to keep those changes in effect in the calling function. The following sample program helps to illustrate this concept.
/ / Fil ename: C18ADD1. CPP
/ / Passi ng by addr ess exampl e. #i ncl ude <i ost r eam. h>
#i ncl ude <st r i ng. h>
change_i t ( char c[ 4] ) ; / / Pr ot ot ype di scussed l at er . mai n( )
{
char name[ 4] =” ABC” ;
change_i t ( name) ; / / Passes by addr ess because
/ / i t i s an arr ay. cout << name << “ \ n” ; / / Call ed f unct i on can
/ / change arr ay.
r et ur n 0;
}
change_i t ( char c[ 4] ) / / You must t ell t he f unct i on
/ / t hat c i s an arr ay.
{
cout << c << “ \ n” ; / / Pr i nt as i t i s passed. st r cpy( c, “ USA”) ; / / Change t he arr ay, bot h
/ / her e and i n mai n() .
r et ur n 0;
}
Here is the output from this program:
ABC USA
At this point, you should have no trouble understanding that the array is passed from mai n() to the function called change_i t () . Even though change_i t () calls the array c, it refers to the same array passed by the mai n() function (name).
Figure 18.4 shows how the array is passed. Although the address of the array—and not its value—is passed from name to c, both arrays are the same.
Figure 18.4. Passing an array by address.
Before going any further, a few additional comments are in order. Because the address of name is passed to the function—even though the array is called c in the receiving function—it is still the same array as name. Figure 18.5 shows how C++ accomplishes this task at the memory-address level.
Figure 18.5. The array being passed is the same array in both functions.
The variable array is referred to as name in mai n() and as c in change_i t () . Because the address of name is copied to the receiving function, the variable is changed no matter what it is called in either
function. Because change_i t () changes the array, the array is changed also in mai n() .
Examples
- You
can now use a function to fill an array with user input. The following function asks users for their first name in the function called get _name() . As users type the name in the array, it is also entered in mai n() ’s array. The mai n() function then passes the array to pr _name() , where it is printed. (If arrays were passed by value, this program would not work. Only the array value would be passed to the called func- tions.)
/ / Fil ename: C18ADD2. CPP
/ / Get a name i n an arr ay, t hen pr i nt i t usi ng
/ / separ at e f unct i ons. #i ncl ude <i ost r eam. h>
get _name( char name[ 25] ) ; / / Pr ot ot ypes di scussed l at er . pr i nt _name( char name[ 25] ) ;
mai n( )
{
char name[ 25];
get _name( name) ; / / Get t he user ’ s name.
pr i nt _name( name) ; / / Pr i nt t he user ’ s name. r et ur n 0;
}
get _name( char name[ 25] ) / / Pass t he arr ay by addr ess.
{
cout << “ What i s your f i r st name? “ ; ci n >> name;
r et ur n 0;
}
pr i nt _name( char name[ 25] )
{
cout << “ \ n\ n Her e you ar e, “ << name; r et ur n 0;
}
When you pass an array, be sure to specify the array’s type in the receiving function’s parameter list. If the previous program declared the passed array with
get _name( char name)
the function get _name() would interpret this as a single character variable, not a character array. You never have to put the array size in brackets. The following statement also works as the first line of get _name() .
get _name( char name[] )
Most C++ programmers put the array size in the brackets to clarify the array size, even though the size is not needed.
- Many
programmers pass character arrays to functions to erase them. Here is a function called cl ear _i t () . It expects two parameters: a character array and the total number of elements declared for that array. The array is passed by address (as are all arrays) and the number of elements, num_el s , is passed by value (as are all nonarrays). When the function finishes, the array is cleared (all its elements are reset to null zero). Subsequent functions that use it can then have an empty array.
cl ear _i t ( char ar a[ 10] , i nt num_el s)
{
i nt ct r ;
f or ( ct r =0; ct r <num_el s; ct r ++)
{ ar a[ ct r ] = ‘ \ 0’ ; } r et ur n 0;
}
The brackets after ar a do not have to contain a number, as described in the previous example. The 10 in this example is simply a placeholder for the brackets. Any value (or no value) would work as well.
You can pass nonarrays by address as well.
Passing Nonarrays by Addres s
You now should see the difference between passing variables by address and by value. Arrays can be passed by address, and nonarrays can be passed by value. You can override the by value default for nonarrays. This is helpful sometimes, but it is not always recommended because the called function can damage values in the called function.
If you want a nonarray variable changed in a receiving function and also want the changes kept in the calling function, you must override the default and pass the variable by address. (You should understand this section better after you learn how arrays and pointers relate.) To pass a nonarray by address, you must precede the argument in the receiving function with an ampersand (&).
This might sound strange to you (and it is, at this point). Few C++ programmers override the default of passing by address. When you learn about pointers later, you should have little need to do so. Most C++ programmers don’t like to clutter their code with these extra ampersands, but it’s nice to know you can override the default if necessary.
The following examples demonstrate how to pass nonarray variables by address.
Examples
- The
following program passes a variable by address from mai n() to a function. The function changes it and returns to mai n() . Because the variable is passed by address, mai n()
recognizes the new value.
/ / Fil ename: C18ADD3. CPP
/ / Demonst r at e passi ng nonarr ays by addr ess. #i ncl ude <i ost r eam. h>
do_f un( i nt &amt ) ; / / Pr ot ot ypes di scussed l at er .
mai n( )
{
i nt amt;
amt = 100; / / Assi gn a val ue i n mai n() . cout << “ I n mai n() , amt i s “ << amt << “ \ n” ;
do_f un( amt ) ; / / Pass amt by addr ess
cout << “ Aft er r et ur n, amt i s “ << amt << “ i n mai n() \ n” ; r et ur n 0;
}
do_f un( i nt &amt ) / / I nf orm f unct i on of
/ / passi ng by addr ess.
{
amt = 85; / / Assi gn new val ue t o amt. cout << “ I n do_f un() , amt i s “ << amt << “ \ n” ;
r et ur n 0;
}
The output from this program follows:
I n mai n() , amt i s 100 I n do_f un() , amt i s 85
Aft er r et ur n, amt i s 85 i n mai n()
Notice that amt changed in the called function. Because it was passed by address, it is changed also in the calling function.
- You
can use a function to get the user’s keyboard values. The mai n() function recognizes those values as long as you pass them by address. The following program calculates the cubic feet in a swimming pool. In one function, it requests the width, length, and depth. In another function, it calcu- lates the cubic feet of water. Finally, in a third function, it prints the answer. The mai n() function is clearly a controlling function, passing variables between these functions by address.
/ / Fil ename: C18POOL. CPP
/ / Cal cul at es t he cubi c f eet i n a swi mmi ng pool . #i ncl ude <i ost r eam. h>
get _val ues( i nt &l engt h, i nt &wi dt h, i nt &dept h) ;
cal c_cubi c( i nt &l engt h, i nt &wi dt h, i nt &dept h, i nt &cubi c) ; pr i nt _cubi c( i nt &cubi c) ;
mai n( )
{
i nt l engt h, wi dt h, dept h, cubi c;
get _val ues( l engt h, wi dt h, dept h) ;
cal c_cubi c( l engt h, wi dt h, dept h, cubi c) ; pr i nt _cubi c( cubi c) ;
r et ur n 0;
}
get _val ues( i nt &l engt h, i nt &wi dt h, i nt &dept h)
{
cout << “ What i s t he pool’ s l engt h? “ ; ci n >> l engt h;
cout << “ What i s t he pool’ s wi dt h? “ ; ci n >> wi dt h;
cout << “ What i s t he pool’ s aver age dept h? “ ; ci n >> dept h;
r et ur n 0;
}
cal c_cubi c( i nt &l engt h, i nt &wi dt h, i nt &dept h, i nt &cubi c)
{
cubi c = ( l engt h) * ( wi dt h) * ( dept h) ; r et ur n 0;
}
pr i nt _cubi c( i nt &cubi c)
{
cout << “ \ nThe pool has “ << cubi c << “ cubi c f eet\ n” ; r et ur n 0;
}
The output follows:
What i s t he pool’ s l engt h? 16 What i s t he pool’ s wi dt h? 32
What i s t he pool’ s aver age dept h? 6 The pool has 3072 cubi c f eet
All variables in a function must be preceded with an amper- sand if they are to be passed by address.
Review Question s
The answers to the review questions are in Appendix B.
-
What
type of variable is automatically passed by address?
-
What type of variable is automatically passed by value?
-
True or false: If a variable is passed by value, it is passed also
by copy.
-
If
a variable is passed to a function by value and the function changes the variable, is it changed in the calling function?
-
If a variable is passed to a function by address and the function
changes the variable, is it changed in the calling function?
-
What
is wrong with the following function?
do_f un( x, y, z)
{
cout << “ The var i abl es ar e “ << x << y << z; r et ur n 0;
}
-
Suppose you pass a nonarray variable and an array to a function at
the same time. What is the default?
-
Both are passed by address.
-
Both are passed by value.
-
One is passed by address and the other is passed by value.
-
Review Exercise s
-
Write
a mai n() function and a second function that mai n() calls. Ask users for their annual income in mai n() . Pass the income to the second function and print a congratulatory message if the user makes more than $50,000 or an encour- agement message if the user makes less.
-
Write
a three-function program, consisting of the following functions:
mai n( )
f un1( )
f un2( )
Declare a 10-element character array in mai n() , fill it with the letters A through J in f un1() , then print that array backwards in f un2() .
- Write
a program whose mai n() function passes a number to a function called pr i nt _ast er() . The pr i nt _ast er() function prints that many asterisks on a line, across the screen. If
pr i nt _ast er() is passed a number greater than 80, display an error because most screens cannot print more than 80 char- acters on the same line. When execution is finished, return control to mai n() and then return to the operating system.
- Write
a function that is passed two integer values by ad- dress. The function should declare a third local variable. Use the third variable as an intermediate variable and swap the values of both passed integers. For example, suppose the calling function passes your function ol d_pay and new_pay
as in
swap_i t ( ol d_pay, new_pay) ;
The swap_i t () function reverses the two values so, when control returns to the calling function, the values of ol d_pay and new_pay are swapped.
Summary
You now have a complete understanding of the various meth- ods for passing data to functions. Because you will be using local variables as much as possible, you have to know how to pass local variables between functions but also keep the variables away from functions that don’t need them.
You can pass data in two ways: by value and by address. When you pass data by value, which is the default method for nonarrays, only a copy of the variable’s contents are passed. If the called function modifies its parameters, those variables are not modified in the calling function. When you pass data by address, as is done with arrays and nonarray variables preceded by an ampersand, the receiving function can change the data in both functions.
Whenever you pass values, you must ensure that they match in number and type. If you don’t match them, you could have prob- lems. For example, suppose you pass an array and a floating-point variable, but in the receiving function, you receive a floating-point variable followed by an array. The data does not reach the receiving function properly because the parameter data types do not match the variables being passed. Chapter 19, “Function Return Values and Prototypes,” shows you how to protect against such disasters by prototyping all your functions.
Function Retur n Values an d
Prototypes
So far, you have passed variables to functions in only one direc- tion—a calling function passed data to a receiving function. You have yet to see how data are passed back from the receiving function to the calling function. When you pass variables by address, the data are changed in both functions—but this is different from passing data back. This chapter focuses on writing function return values that improve your programming power.
After you learn to pass and return values, you have to prototype your own functions as well as C++’s built-in functions, such as cout and ci n . By prototyping your functions, you ensure the accuracy of passed and returned values.
This chapter introduces you to the following:
-
Returning values from functions
-
Prototyping functions
-
Understanding header files
By returning values from functions, you make your functions fully modular. They can now stand apart from the other functions.
They can receive and return values and act as building blocks that compose your complete application.
Function Return Value s
Until now, all functions in this book have been subroutines or subfunctions. A C++ subroutine is a function that is called from another function, but it does not return any values. The difference between subroutines and functions is not as critical in C++ as it is in other languages. All functions, whether they are subroutines or functions that return values, are defined in the same way. You can pass variables to each of them, as you have seen throughout this section of the book.
Put the return value
at the end of the
r et ur n statement.
Functions that return values offer you a new approach to programming. In addition to passing data one-way, from calling to receiving function, you can pass data back from a receiving function to its calling function. When you want to return a value from a function to its calling function, put the return value after the r et ur n statement. To clarify the return value even more, many program- mers put parentheses around the return value, as shown in the following syntax:
r et ur n (r et ur n val ue) ;
The calling function must have a use for the return value. For example, suppose you wrote a function that calculated the average of any three integer variables passed to it. If you return the average, the calling function has to receive that return value. The following sample program helps to illustrate this principle.
/ / Fil ename: C19AVG. CPP
/ / Cal cul at es t he aver age of t hr ee i nput val ues. #i ncl ude <i ost r eam. h>
i nt cal c_av( i nt num1, i nt num2, i nt num3) ; // Pr ot ot ype
mai n( )
{
i nt num1, num2, num3;
i nt avg; / / Hol ds t he r et ur n val ue.
cout << “ Pl ease t ype t hr ee number s ( such as 23 54 85) “ ; ci n >> num1 >> num2 >> num3;
/ / Call t he f unct i on, pass t he number s,
/ / and accept t he r et ur n val ue amount. avg = cal c_av( num1, num2, num3) ;
cout << “ \ n\ nThe aver age i s “ << avg; / / Pr i nt t he
/ / r et ur n val ue.
r et ur n 0;
}
i nt cal c_av( i nt num1, i nt num2, i nt num3)
{
i nt l ocal _avg; / / Hol ds t he aver age f or t hese number s. l ocal _avg = ( num1+num2+num3) / 3;
r et ur n ( l ocal _avg) ;
}
Here is a sample output from the program:
Pl ease t ype t hr ee number s ( such as 23 54 85) 30 40 50
The aver age i s 40
Study this program carefully. It is similar to many you have seen, but a few additional points have to be considered now that the function returns a value. It might help to walk through this program a few lines at a time.
The first part of mai n() is similar to other programs you have seen. It declares its local variables: three for user input and one for the calculated average. The cout and ci n are familiar to you. The function call to cal c_av() is also familiar; it passes three variables
Put the function’s return type before its name. If you don’t specify a return type, i nt is the default.
(num1, num2, and num3) by value to cal c_av() . (If it passed them by address, an ampersand (&) would have to precede each argument, as discussed in Chapter 18.)
The receiving function, cal c_av() , seems similar to others you have seen. The only difference is that the first line, the function’s definition line, has one addition—the i nt before its name. This is the type of the return value. You must always precede a function name with its return data type. If you do not specify a type, C++ assumes a type of i nt . Therefore, if this example had no return type, it would work just as well because an i nt return type would be assumed.
Because the variable being returned from cal c_av() is an inte- ger, the i nt return type is placed before cal c_av() ’s name.
You can see also that the return statement of cal c_av() includes the return value, l ocal _avg . This is the variable being sent back to the calling function, mai n() . You can return only a single variable to a calling function.
Even though a function can receive more than one parameter, it can return only a single value to the calling function. If a receiving function is modifying more than one value from the calling function, you must pass the parameters by address; you cannot return mul- tiple values using a r et ur n statement.
After the receiving function, cal c_av() , returns the value, mai n() must do something with that returned value. So far, you have seen function calls on lines by themselves. Notice in mai n() that the function call appears on the right side of the following assignment statement:
avg = cal c_av( num1, num2, num3) ;
When the cal c_av() function returns its value—the average of the three numbers—that value replaces the function call. If the average computed in cal c_av() is 40, the C++ compiler interprets the following statement in place of the function call:
avg = 40;
You typed a function call to the right of the equal sign, but the program replaces a function call with its return value when the r et ur n takes place. In other words, a function that returns a value
becomes that value. You must put such a function anywhere you put any variable or literal (usually to the right of an equal sign, in an expression, or in cout ). The following is an incorrect way of calling cal c_av( ) :
cal c_av( num1, num2, num3) ;
If you did this, C++ would have nowhere to put the return value.
Examples
- The
following program passes a number to a function called doub() . The function doubles the number and returns the result.
/ / Fil ename: C19DOUB. CPP
/ / Doubl es t he user ’ s number . #i ncl ude <i ost r eam. h>
i nt doub ( i nt num) ; mai n( )
{
i nt number ; / / Hol ds user ’ s i nput. i nt d_number ; / / Hol ds doubl e t he user ’ s i nput.
cout << “ What number do you want doubl ed? “ ; ci n >> number ;
d_number = doub( number) ; / / Assi gns r et ur n val ue. cout << number << “ doubl ed i s “ << d_number ;
r et ur n 0;
}
i nt doub( i nt num)
{
i nt d_num;
d_num = num * 2; / / Doubl es t he number .
r et ur n ( d_num) ; / / Ret ur ns t he r esul t.
}
The program produces output such as this:
What number do you want doubl ed? 5
5 doubl ed i s 10
- Function return values can be used anywhere literals, vari- ables,
and expressions are used. The following program is similar to the previous one. The difference is in mai n() .
The function call is performed not on a line by itself, but from a cout . This is a nested function call. You call the built- in function cout using the return value from one of the program’s functions named doub() . Because the call to doub() is replaced by its return value, the cout has enough informa- tion to proceed as soon as doub() returns. This gives mai n() less overhead because it no longer needs a variable called d_number , although you must use your own judgment as to whether this program is easier to maintain. Sometimes it is wise to include function calls in other expressions; other times it is clearer to call the function and assign its return value to a variable before using it.
/ / Fil ename: C19DOUB2. CPP
/ / Doubl es t he user ’ s number . #i ncl ude <i ost r eam. h>
i nt doub( i nt num) ; / / Pr ot ot ype
mai n( )
{
i nt number ; / / Hol ds user ’ s i nput. cout << “ What number do you want doubl ed? “ ;
ci n >> number ;
/ / The t hi r d cout par amet er i s
/ / r epl aced wi t h a r et ur n val ue.
cout << number << “ doubl ed i s “ << doub( number) ;
r et ur n 0;
}
i nt doub( i nt num)
{
i nt d_num;
d_num = num * 2; / / Doubl e t he number .
r et ur n ( d_num) ; / / Ret ur n t he r esul t.
}
- The following program asks the user for a number. That number is
then passed to a function called sum() , which adds the numbers from 1 to that number. In other words, if the user types a 6, the function returns the result of the following calculation:
1 + 2 + 3 + 4 + 5 + 6
This is known as the sum of the digits calculation, and it is sometimes used for depreciation in accounting.
/ / Fil ename: C19SUMD. CPP
/ / Comput e t he sum of t he di gi t s. #i ncl ude <i ost r eam. h>
i nt sum( i nt num) ; / / Pr ot ot ype
mai n( )
{
i nt num, sumd;
cout << “ Pl ease t ype a number : “ ; ci n >> num;
sumd = sum( num) ;
cout << “ The sum of t he di gi t s i s “ << sumd; r et ur n 0;
}
i nt sum( i nt num)
{
i nt ct r ; / / Local l oop count er . i nt sumd=0; / / Local t o t hi s f unct i on. i f ( num <= 0) / / Check whet her par amet er i s t oo small .
{ sumd = num; } / / Ret ur ns par amet er i f t oo small . el se
{ f or ( ct r =1; ct r <=num; ct r ++)
{ sumd += ct r ; }
}
r et ur n( sumd) ;
}
The following is a sample output from this program:
Pl ease t ype a number : 6
The sum of t he di gi t s i s 21
- The following program contains two functions that return values. The
first function, maxi mum() , returns the larger of two numbers entered by the user. The second one, mi ni mum() , returns the smaller.
/ / Fil ename: C19MI NMX. CPP
/ / Fi nds mi ni mum and maxi mum val ues i n f unct i ons. #i ncl ude <i ost r eam. h>
i nt maxi mum( i nt num1, i nt num2) ; / / Pr ot ot ypes i nt mi ni mum( i nt num1, i nt num2) ;
mai n( )
{
i nt num1, num2; / / User ’ s t wo number s. i nt mi n, max;
cout << “ Pl ease t ype t wo number s ( such as 46 75) “ ; ci n >> num1 >> num2;
max = maxi mum( num1, num2) ; / / Assi gn t he r et ur n mi n = mi ni mum( num1, num2) ; / / val ue of each
/ / f unct i on t o var i abl es.
cout << “ The mi ni mum number i s “ << mi n << “ \
n” ; cout << “ The maxi mum number i s “ << max << “
\ n” ; r et ur n 0;
}
i nt maxi mum( i nt num1, i nt num2)
{
i nt max; / / Local t o t hi s f unct i on onl y. max = ( num1 > num2) ? ( num1) : ( num2) ;
r et ur n ( max) ;
}
i nt mi ni mum( i nt num1, i nt num2)
{
i nt mi n; / / Local t o t hi s f unct i on onl y. mi n = ( num1 < num2) ? ( num1) : ( num2) ;
r et ur n ( mi n) ;
}
Here is a sample output from this program:
Pl ease t ype t wo number s ( such as 46 75) 72 55 The mi ni mum number i s 55
The maxi mum number i s 72
If the user types the same number, mi ni mum and maxi mum are the same.
These two functions can be passed any two integer values. In such a simple example as this one, the user certainly already knows which number is lower or higher. The point of such an example is to show how to code return values. You might want to use similar functions in a more useful application, such as finding the highest paid employee from a payroll disk file.
Function Prototype s
The word prototype is sometimes defined as a model. In C++, a function prototype models the actual function. Before completing
C++ assumes functions returni nt unless you put a different data return type, or use the
voi d keyword.
your study of functions, parameters, and return values, you must understand how to prototype each function in your program.
C++ requires that you prototype all functions in your program. When prototyping, you inform C++ of the function’s parameter types and its return value, if any.
To prototype a function, copy the function’s definition line to the top of your program (immediately before or after the #i ncl ude
<i ost r eam. h> line). Place a semicolon at the end of the function definition line, and you have the prototype. The definition line (the function’s first line) contains the return type, the function name, and the type of each argument, so the function prototype serves as a model of the function that follows.
If a function does not return a value, or if that function has no arguments passed to it, you should still prototype it. Place the keyword voi d in place of the return type or the parameters. mai n() is the only function that you do not have to prototype because it is self- prototyping; meaning mai n() is not called by another function. The first time mai n() appears in your program (assuming you follow the standard approach and make mai n() your program’s first function), it is executed.
If a function returns nothing, voi d must be its return type. Put voi d in the argument parentheses of function prototypes with no arguments. All functions must match their prototypes.
All mai n() functions in this book have returned a 0. Why? You now know enough to answer that question. Because mai n() is self- prototyping, and because the voi d keyword never appeared before mai n() in these programs, C++ assumed an i nt return type. All C++ functions prototyped as returning i nt or those without any return data type prototype assume i nt . If you wanted to not put r et ur n 0; at the end of mai n() ’s functions, you must insert voi d before mai n() as in:
voi d mai n() / / mai n() sel f - pr ot ot ypes t o r et ur n not hi ng.
You can look at a statement and tell whether it is a prototype or a function definition (the function’s first line) by the semicolon on the end. All prototypes, unless you make mai n() self-prototype, end with a semicolon.
Prototype for Safet y
Prototyping protects you from programming mistakes. Sup- pose you write a function that expects two arguments: an integer followed by a floating-point value. Here is the first line of such a function:
my_f un( i nt num, f l oat amount )
What if you passed incorrect data types to my_f un() ? If you were to call this function by passing it two literals, a floating-point followed by an integer, as in
my_f un( 23. 43, 5) ; / / Call t he my_f un() f unct i on.
the function would not receive correct parameters. It is expecting an integer followed by a floating-point, but you did the opposite and sent it a floating-point followed by an integer.
Prototyping protects your programs from function program- ming errors.
In regular C programs, mismatched arguments such as these generate no error message even though the data are not passed correctly. C++ requires prototypes so you cannot send the wrong data types to a function (or expect the wrong data type to be returned). Prototyping the previous function results in this:
voi d my_f un( i nt num, f l oat amount ) ; / / Pr ot ot ype
In doing so, you tell the compiler to check this function for accuracy. You inform the compiler to expect nothing after the r et ur n statement, not even 0, (due to the voi d keyword) and to expect an integer followed by a floating-point in the parentheses.
If you break any of the prototype’s rules, the compiler informs you of the problem and you can correct it.
Prototype All Function s
You should prototype every function in your program. As just described, the prototype defines (for the rest of the program) which functions follow, their return types, and their parameter types. You should prototype C++’s built-in functions also, such as pr i ntf () and scanf () if you use them.
Header files contain built-in function prototypes.
Think about how you prototype pr i ntf () . You don’t always pass it the same types of parameters because you print different data with each pr i ntf () . Prototyping functions you write is easy: The prototype is basically the first line in the function. Prototyping functions you do not write might seem difficult, but it isn’t—you have already done it with every program in this book!
The designers of C++ realized that all functions have to be prototyped. They realized also that you cannot prototype built-in functions, so they did it for you and placed the prototypes in header files on your disk. You have been including the pr i ntf () and scanf () prototypes in each program that used them in this book with the following statement:
#i ncl ude <st di o. h>
Inside the stdio.h file is a prototype of many of C++’s input and output functions. By having prototypes of these functions, you ensure that they cannot be passed bad values. If someone attempts to pass incorrect values, C++ catches the problem.
Because pr i ntf () and scanf () are not used very often in C++, the cout and ci n operators have their own header file called iostream.h that you have seen included in this book’s programs as well. The iostream.h file does not actually include prototypes for cout and ci n because they are operators and not functions, but iostream.h does include some needed definitions to make cout and ci n work.
Remember too that iomanip.h has to be included if you use a set w or set pr eci si on modifier in cout . Any time you use a new built- in C++ function or a manipulating operator, check your compiler’s manual to find the name of the prototype file to include.
Prototyping is the primary reason why you should always include the matching header file when you use C++’s built-in functions. The st r cpy() function you saw in previous chapters requires the following line:
#i ncl ude <st r i ng. h>
This is the header file for the st r cpy() function. Without it, the program does not work.
Examples
- Prototype all functions in all programs except mai n() . Even
mai n() must be prototyped if it returns nothing (not even 0).
The following program includes two prototypes: one for mai n() because it returns nothing, and one for the built-in pr i ntf () and scanf () functions.
/ / Fil ename: C19PRO1. CPP
/ / Cal cul at es sal es t ax on a sal e
#i ncl ude <st di o. h> / / Pr ot ot ype buil t - i n f unct i ons. voi d mai n( voi d) ;
voi d mai n( voi d)
{
f l oat t ot al _sal e;
f l oat t ax_r at e = . 07; / / Assume seven per cent
/ / t ax r at e.
pr i ntf (“ What i s t he sal e amount ? “) ; scanf (“ %f ” , &t ot al _sal e) ;
t ot al _sal e += ( t ax_r at e * t ot al _sal e) ;
pr i ntf (“ The t ot al sal e i s %. 2f ” , t ot al _sal e) ; r et ur n; / / No 0 r equi r ed!
}
Notice that mai n() ’s r et ur n statement needed only a semi- colon after it. As long as you prototype mai n() with a voi d return type, the last line in mai n() can be r et ur n; instead of having to type r et ur n 0; each time.
- The
following program asks the user for a number in mai n() , and passes that number to ascii () . The ascii () function returns the ASCII character that matches the user’s number. This example illustrates a char act er return type. Functions can return any data type.
/ / Fil ename: C19ASC. CPP
/ / Pr i nt s t he ASCI I char act er of t he user ’ s number .
/ / Pr ot ot ypes f oll ow. #i ncl ude <i ost r eam. h> char ascii ( i nt num) ;
voi d mai n()
{
i nt num;
char asc_char ;
cout << “ Ent er an ASCI I number ? “ ; ci n >> num;
asc_char = ascii ( num) ;
cout << “ The ASCI I char act er f or “ << num
<< “ i s “ << asc_char ; r et ur n;
}
char ascii ( i nt num)
{
char asc_char ;
asc_char = char( num) ; / / Type cast t o a char act er . r et ur n ( asc_char) ;
}
The output from this program follows:
Ent er an ASCI I number ? 67
The ASCI I char act er f or 67 i s C
- Suppose you have to calculate net pay for a company. You find
yourself multiplying the hours worked by the hourly pay, then deducting taxes to compute the net pay. The following program includes a function that does this for you. It requires three arguments: the hours worked, the hourly pay, and the tax rate (as a floating-point decimal, such as .30 for 30 percent). The function returns the net pay. The mai n() calling program tests the function by sending three different payroll values to the function and printing the three return values.
/ / Fil ename: C19NPAY. CPP
/ / Def i nes a f unct i on t hat comput es net pay.
#i ncl ude <i ost r eam. h> / / Needed f or cout and ci n. voi d mai n( voi d) ;
f l oat net payf un( f l oat hour s, f l oat r at e, f l oat t axr at e) ;
voi d mai n( voi d)
{
f l oat net _pay;
net _pay = net payf un( 40. 0, 3. 50, . 20) ;
cout << “ The pay f or 40 hour s at $3. 50/ hr . , and a 20% “
<< “ t ax r at e i s $” ; cout << net _pay << “ \
n” ;
net _pay = net payf un( 50. 0, 10. 00, . 30) ;
cout << “ The pay f or 50 hour s at $10. 00/ hr . , and a 30% “
<< “ t ax r at e i s $” ; cout << net _pay << “ \
n” ;
net _pay = net payf un( 10. 0, 5. 00, . 10) ;
cout << “ The pay f or 10 hour s at $5. 00/ hr . , and a 10% “
<< “ t ax r at e i s $” ; cout << net _pay << “ \
n” ;
r et ur n;
}
f l oat net payf un( f l oat hour s, f l oat r at e, f l oat t axr at e)
{
f l oat gr oss_pay, t axes, net _pay; gr oss_pay = ( hour s * r at e) ;
t axes = ( t axr at e * gr oss_pay) ; net _pay = ( gr oss_pay - t axes) ; r et ur n ( net _pay) ;
}
Review Question s
The answers to the review questions are in Appendix B.
-
How
do you declare function return types?
-
What is the maximum number of return values a function can return?
-
What are header files for?
-
What
is the default function return type?
-
True or false: a function that returns a value can be passed only a
single parameter.
-
How
do prototypes protect the programmer from bugs?
-
Why don’t you have to return global variables?
-
What is the return type, given the following function prototype?
f l oat my_f un( char a, i nt b, f l oat c) ;
How many parameters are passed to my_f un() ? What are their types?
Review Exercise s
-
Write
a program that contains two functions. The first function returns the square of the integer passed to it, and the second function returns the cube. Prototype mai n() so you do not have to return a value.
-
Write
a function that returns the double-precision area of a circle, given that a double-precision radius is passed to it.
The formula for calculating the area of a circle is
ar ea = 3. 14159 * (r adi us * r adi us)
- Write
a function that returns the value of a polynomial given this formula:
9x4 + 15x2 + x1
Assume x is passed from mai n() and it is supplied by the user.
Summary
You learned how to build your own collection of functions. When you write a function, you might want to use it in more than one program—there is no need to reinvent the wheel. Many pro- grammers write useful functions and use them in more than one program.
You now understand the importance of prototyping functions. You should prototype all your own functions, and include the appropriate header file when you use one of C++’s built-in func- tions. Furthermore, when a function returns a value other than an integer, you must prototype so C++ recognizes the noninteger return value.
Default Argument s and Functio n
Overloading
All functions that receive arguments do not have to be sent values. C++ enables you to specify default argument lists. You can write functions that assume argument values even if you do not pass them any arguments.
C++ also enables you to write more than one function with the same function name. This is called overloading functions. As long as their argument lists differ, the functions are differentiated by C++.
This chapter introduces you to the following:
-
Default argument lists
-
Overloaded functions
-
Name-mangling
Default argument lists and overloaded functions are not avail- able in regular C. C++ extends the power of your programs by providing these time-saving procedures.
Default Argument List s
Suppose you were writing a program that has to print a message on-screen for a short period of time. For instance, you pass a function an error message stored in a character array and the function prints the error message for a certain period of time.
The prototype for such a function can be this:
voi d pr _msg( char not e[] ) ;
Therefore, to request that pr _msg() print the line “ Tur n pr i nt er on” , you call it this way:
pr _msg(“ Tur n pr i nt er on”) ; / / Passes a message t o be pr i nt ed.
This command prints the message “ Turn pr i nt er on” for a period of five seconds or so. To request that pr _msg() print the line “ Pr ess any key t o cont i nue... ” , you call it this way:
pr _msg(“ Pr ess a key t o cont i nue... ”) ; / / Passes a message.
As you write more of the program, you begin to realize that you are printing one message, for instance the “ Turn pr i nt er on” message, more often than any other message. It seems as if the pr _msg() function is receiving that message much more often than any other. This might be the case if you were writing a program that printed many reports to the printer. You still will use pr _msg() for other delayed messages, but the “ Tur n pr i nt er on” message is most frequently used.
Instead of calling the function over and over, typing the same message each time, you can set up the prototype for pr _msg() so it defaults to the “ Tur n pr i nt er on” in this way:
voi d pr _msg( char not e[] =” Tur n pr i nt er on”) ; / / Pr ot ot ype
List default argument values in the prototype.
After prototyping pr _msg() with the default argument list, C++ assumes you want to pass “ Tur n pr i nt er on” to the function unless you override the default by passing something else to it. For in- stance, in mai n() , you call pr _msg() this way:
pr _msg() ; / / C++ assumes you mean “ Tur n pr i nt er on” .
This makes your programming job easier. Because most of the time you want pr _msg() to print “ Tur n pr i nt er on” the default
argument list takes care of the message and you do not have to pass the message when you call the function. However, those few times when you want to pass something else, simply pass a different message. For example, to make pr _msg() print “ I ncorr ect val ue” you type:
pr _msg(“ I ncorr ect val ue”) ; / / Pass a new message.
Multiple Default Argument s
You can specify more than one default argument in the proto- type list. Here is a prototype for a function with three default arguments:
f l oat f unct 1( i nt i =10, f l oat x=7. 5, char c=’ A’ ) ;
There are several ways you can call this function. Here are some samples:
f unct 1( ) ;
All default values are assumed.
f unct 1( 25) ;
A 25 is sent to the integer argument, and the default values are assumed for the rest.
f unct 1( 25, 31. 25) ;
A 25 is sent to the integer argument, 31. 25 to the floating-point argument, and the default value of ‘ A’ is assumed for the character argument.
Examples
- Here
is a complete program that illustrates the message- printing function described earlier in this chapter. The mai n() function simply calls the delayed message-printing function three times, each time passing it a different set of argument lists.
/ / Fil ename: C20DEF1. CPP
/ / I ll ust r at es def aul t ar gument li st. #i ncl ude <i ost r eam. h>
voi d pr _msg( char not e[] =” Tur n pr i nt er on”) ; / / Pr ot ot ype. voi d mai n()
{
pr _msg() ; / / Pr i nt s def aul t message. pr _msg(“ A new message”) ; / / Pr i nt s anot her message.
pr _msg() ; / / Pr i nt s def aul t message agai n. r et ur n;
}
voi d pr _msg( char not e[] ) / / Onl y pr ot ot ype cont ai ns def aul t s.
{
l ong i nt del ay;
cout << not e << “ \ n” ;
f or ( del ay=0; del ay<500000; del ay++)
{ ; / * Do not hi ng whil e wai t i ng * / } r et ur n;
}
The program produces the following output:
Tur n pr i nt er on A new message Tur n pr i nt er on
The delay loop causes each line to display for a couple of seconds or more, depending on the speed of your computer, until all three lines print.
- The
following program illustrates the use of defaulting several arguments. mai n() calls the function de_f un() five times, sending de_f un() five sets of arguments. The de_f un() function prints five different things depending on mai n() ’s argument list.
/ / Fil ename: C20DEF2. CPP
/ / Demonst r at es def aul t ar gument li st wi t h sever al par amet er s. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
voi d de_f un( i nt i =5, l ong j =40034, f l oat x=10. 25,
char ch=’ Z’ , doubl e d=4. 3234) ; / / Pr ot ot ype
voi d mai n()
{
de_f un() ; / / All def aul t s used.
de_f un( 2) ; / / Fi r st def aul t overr i dden.
de_f un( 2, 75037) ; / / Fi r st and second def aul t overr i dden. de_f un( 2, 75037, 35. 88) ; / / Fi r st , second, and t hi r d de_f un( 2, 75037, 35. 88, ‘ G’ ) ; / / Fi r st , second, t hi r d,
/ / and f our t h
r et ur n;
}
voi d de_f un( i nt i , l ong j , f l oat x, char ch, doubl e d)
{
cout << set pr eci si on( 4) << “ i : “ << i << “ “ << “ j : “ << j ; cout << “ x: “ << x << “ “ << “ ch: “ << ch;
cout << “ d: “ << d << “ \ n” ; r et ur n;
}
Here is the output from this program:
i : |
5 |
j : |
40034 |
x: |
10. 25 |
ch: |
Z |
d: |
4. 3234 |
---|---|---|---|---|---|---|---|---|---|
i : |
2 |
j : |
40034 |
x: |
10. 25 |
ch: |
Z |
d: |
4. 3234 |
i : |
2 |
j : |
75037 |
x: |
10. 25 |
ch: |
Z |
d: |
4. 3234 |
i : |
2 |
j : |
75037 |
x: |
35. 88 |
ch: |
Z |
d: |
4. 3234 |
i : |
2 |
j : |
75037 |
x: |
35. 88 |
ch: |
G |
d: |
4. 3234 |
i : |
2 |
j : |
75037 |
x: |
35. 88 |
ch: |
G |
d: |
0. 0023 |
Notice that each call to de_f un() produces a different output because mai n() sends a different set of parameters each time mai n() calls de_f un() .
Overloaded Function s
Unlike regular C, C++ enables you to have more than one function with the same name. In other words, you can have three functions called abs() in the same program. Functions with the same names are called overloaded functions. C++ requires that each overloaded function differ in its argument list. Overloaded func- tions enable you to have similar functions that work on different types of data.
For example, suppose you wrote a function that returned the absolute value of whatever number you passed to it. The absolute value of a number is its positive equivalent. For instance, the absolute value of 10.25 is 10.25 and the absolute value of –10.25 is 10.25.
Absolute values are used in distance, temperature, and weight calculations. The difference in the weights of two children is always
positive. If Joe weighs 65 pounds and Mary weighs 55 pounds, their difference is a positive 10 pounds. You can subtract the 65 from 55 (–10) or 55 from 65 (+10) and the weight difference is always the absolute value of the result.
Suppose you had to write an absolute-value function for inte- gers, and an absolute-value function for floating-point numbers. Without function overloading, you need these two functions:
i nt i abs( i nt i ) / / Ret ur ns absol ut e val ue of an i nt eger .
{
i f ( i < 0)
{ r et ur n ( i * - 1) ; } / / Makes posi t i ve. el se
{ r et ur n ( i ) ; } / / Al r eady posi t i ve.
}
f l oat f abs( f l oat x) / / Ret ur ns absol ut e val ue of a f l oat.
{
i f ( x < 0. 0)
{ r et ur n ( x * - 1. 0) ; } / / Makes posi t i ve. el se
{ r et ur n ( x) ; } / / Al r eady posi t i ve.
}
Without overloading, if you had a floating-point variable for
which you needed the absolute value, you pass it to the f abs()
function as in:
ans = f abs( wei ght ) ;
If you needed the absolute value of an integer variable, you pass it to the i abs() function as in:
i ans = i abs( age) ;
Because the code for these two functions differ only in their parameter lists, they are perfect candidates for overloaded func- tions. Call both functions abs() , prototype both of them, and code each of them separately in your program. After overloading the two functions (each of which works on two different types of parameters with the same name), you pass your floating-point or integer value to abs() . The C++ compiler determines which function you wanted to call.
This process simplifies your programming considerably. In- stead of having to remember several different function names, you only have to remember one function name. C++ passes the argu- ments to the proper function.
Examples
- Here
is the complete absolute value program described in the previous text. Notice that both functions are prototyped. (The two prototypes signal C++ that it must perform name- mangling to determine the correct function names to call.)
/ / Fil ename: C20OVF1. CPP
/ / Over l oads t wo absol ut e val ue f unct i ons.
#i ncl ude <i ost r eam. h> / / Pr ot ot ype cout and ci n.
#i ncl ude <i omani p. h> / / Pr ot ot ype set pr eci si on( 2) .
i nt abs( i nt i ) ; / / abs() i s over l oaded t wi ce
f l oat abs( f l oat x) ; / / as shown by t hese pr ot ot ypes.
voi d mai n()
{
i nt i ans; / / To hol d r et ur n val ues. f l oat f ans;
i nt i = - 15; / / To pass t o t he t wo over l oaded f unct i ons. f l oat x = - 64. 53;
i ans = abs( i ) ; / / C++ call s t he i nt eger abs() .
cout << “ I nt eger absol ut e val ue of - 15 i s “ << i ans << “ \ n” ;
f ans = abs( x) ; / / C++ call s t he f l oat i ng- poi nt abs() . cout << “ Fl oat absol ut e val ue of - 64. 53 i s “ <<
set pr eci si on( 2) << f ans << “ \ n” ;
/ / Not i ce t hat you no l onger have t o keep t r ack of t wo
/ / di ff er ent names. C++ call s t he appr opr i at e
/ / f unct i on t hat mat ches t he par amet er s. r et ur n;
}
i nt abs( i nt i ) / / I nt eger absol ut e val ue f unct i on
{
i f ( i < 0)
{ r et ur n ( i * - 1) ; } / / Makes posi t i ve. el se
{ r et ur n ( i ) ; } / / Al r eady posi t i ve.
}
f l oat abs( f l oat x) / / Fl oat i ng- poi nt absol ut e val ue f unct i on
{
i f ( x < 0. 0)
{ r et ur n ( x * - 1. 0) ; } / / Makes posi t i ve. el se
{ r et ur n ( x) ; } / / Al r eady posi t i ve.
}
The output from this program follows:
I nt eger absol ut e val ue of - 15 i s 15
Fl oat absol ut e val ue of - 64. 53 i s 64. 53
- As you write more and more C++ programs, you will see many uses for
overloaded functions. The following program is a demonstration program showing how you can build your own output functions to suit your needs. mai n() calls three functions named out put () . Each time it’s called, mai n() passes a different value to the function.
When mai n() passes out put () a string, out put () prints the string, formatted to a width (using the set w() manipulator described in Chapter 7, “Simple Input/ Output”) of 30 characters. When mai n() passes out put () an integer, out put () prints the integer with a width of five. When mai n() passes out put () a floating-point value, out put () prints the value to two decimal places and generalizes the output of different types of data. You do not have to format your own data. out put () properly formats the data and you only have to
remember one function name that outputs all three types of data.
/ / Fil ename: C20OVF2. CPP
/ / Out put s t hr ee di ff er ent t ypes of
/ / dat a wi t h same f unct i on name. #i ncl ude <i ost r eam. h>
#i ncl ude <i omani p. h>
voi d out put ( char [] ) ; / / Pr ot ot ypes f or over l oaded f unct i ons. voi d out put ( i nt i ) ;
voi d out put ( f l oat x) ;
voi d mai n()
{
char name[ ] = “ C++ By Exampl e makes C++ easy! ” ; i nt i val ue = 2543;
f l oat f val ue = 39. 4321;
out put ( name) ; / / C++ chooses t he appr opr i at e f unct i on. out put ( i val ue) ;
out put ( f val ue) ;
ret urn;
}
voi d out put ( char name[] )
{
cout << set w( 30) << name << “ \ n” ;
/ / The wi dt h t r uncat es st r i ng i f i t i s l onger t han 30. r et ur n;
}
voi d out put ( i nt i val ue)
{
cout << set w( 5) << i val ue << “ \ n” ;
/ / Just pr i nt ed i nt eger wi t hi n a wi dt h of f i ve spaces. r et ur n;
}
voi d out put ( f l oat f val ue)
{
cout << set pr eci si on( 2) << f val ue << “ \ n” ;
/ / Li mi t ed t he f l oat i ng- poi nt val ue t o t wo deci mal pl aces. r et ur n;
}
Here is the output from this program:
C++ By Exampl e makes C++ easy! 2543
39. 43
Each of the three lines, containing three different lines of information, was printed with the same function call.
Review Question s
The answers to the review questions are in Appendix B.
-
Where
in the program do you specify the defaults for default argument lists?
-
What is the term for C++ functions that have the same name?
-
Does
name-mangling help support default argument lists or overloaded functions?
-
True or false: You can specify only a single default argument.
-
Fix
the following prototype for a default argument list.
voi d my_f un( i nt i =7, f l oat x, char ch=’ A’ ) ;
- True or false: The following prototypes specify overloaded
functions:
i nt sq_r t ( i nt n) ; f l oat sq_r t ( i nt n) ;
Review Exercise s
-
Write
a program that contains two functions. The first function returns the square of the integer passed to it, and the second function returns the square of the float passed to it.
-
Write
a program that computes net pay based on the values the user types. Ask the user for the hours worked, the rate per hour, and the tax rate. Because the majority of employees work 40 hours per week and earn $5.00 per hour, use these values as default values in the function that computes the net pay. If the user presses Enter in response to your ques- tions, use the default values.
Summary
Default argument lists and overloaded functions speed up your programming time. You no longer have to specify values for common arguments. You do not have to remember several different names for those functions that perform similar routines and differ only in their data types.
The remainder of this book elaborates on earlier concepts so you can take advantage of separate, modular functions and local data. You are ready to learn more about how C++ performs input and output. Chapter 21, “Device and Character Input/ Output,” teaches you the theory behind I/ O in C++, and introduces more built-in functions.