He was a local boy, not known outside his home town.
COMMON SAYING
In the last section we advocated using functions as if they were black boxes. In order to define a function so that it can be used as a black box, you often need to give the function variables of its own that do not interfere with the rest of your program. The variables that “belong to” a function are called local variables. As we will see, these variables simply conform to the scope rule for nested blocks described in Chapter 3. In this section we take another look at scoping with an emphasis on local variables and how to use them.
Look back at the program in Display 4.1. It includes a call to the predefined function sqrt
. We did not need to know anything about the details of the function definition for sqrt
in order to use this function. In particular, we did not need to know what variables were declared in the definition of sqrt
. A function that you define is no different. Variable declarations in function definitions that you write are as separate as those in the function definitions for the predefined functions. Variable declarations within a function definition are the same as if they were variable declarations in another program. If you declare a variable in a function definition and then declare another variable of the same name in the main
part of your program (or in the body of some other function definition), then these two variables are two different variables, even though they have the same name. Let’s look at a program that does have a variable in a function definition with the same name as another variable in the program.
The program in Display 4.11 has two variables named averagePea
; one is declared and used in the function definition for the function estTotal
, and the other is declared and used in the main
part of the program. The variable averagePea
in the function definition for estTotal
and the variable averagePea
in the main
part of the program are two different variables. It is the same as if the function estTotal
were a predefined function. The two variables named averagePea
will not interfere with each other any more than two variables in two completely different programs would. When the variable averagePea
is given a value in the function call to estTotal
, this does not change the value of the variable in the main
part of the program that is also named averagePea
. (The details of the program in Display 4.11, other than this coincidence of names, are explained in the Programming Example section that follows this section.)
Local Variables
1 //Computes the average yield on an experimental pea growing patch.
2 #include <iostream>
3 using namespace std;
4
5 double estTotal(int minPeas, int maxPeas, int podCount);
6 //Returns an estimate of the total number of peas harvested.
7 //The formal parameter podCount is the number of pods.
8 //The formal parameters minPeas and maxPeas are the minimum
9 //and maximum number of peas in a pod.
10
11 int main( )
12 {
13 int maxCount, minCount, podCount;
14 double averagePea, yield;
15
16 cout << "Enter minimum and maximum number of peas in a pod: ";
17 cin >> minCount >> maxCount;
18 cout << "Enter the number of pods: ";
19 cin >> podCount;
20 cout << "Enter the weight of an average pea (in ounces): ";
21 cin >> averagePea;
22
23 yield =
24 estTotal(minCount, maxCount, podCount) * averagePea;
25
26 cout.setf(ios::fixed);
27 cout.setf(ios::showpoint);
28 cout.precision(3);
29 cout << "Min number of peas per pod = " << minCount << endl
30 << "Max number of peas per pod = " << maxCount << endl
31 << "Pod count = " << podCount << endl
32 << "Average pea weight = "
33 << averagePea << " ounces" << endl
34 << "Estimated average yield = " << yield << " ounces"
35 << endl;
36
37 return 0;
38 }
39
40 double estTotal(int minPeas, int maxPeas, int podCount)
41 {
42 double averagePea;
43
44 averagePea = (maxPeas + minPeas)/2.0;
45 return (podCount * averagePea);
46 }
Sample Dialogue
Enter minimum and maximum number of peas in a pod: 4 6 Enter the number of pods: 10 Enter the weight of an average pea (in ounces): 0.5 Min number of peas per pod = 4 Max number of peas per pod = 6 Pod count = 10 Average pea weight = 0.500 ounces Estimated average yield = 25.000 ounces
This variable named averagePea
is local to the main part of the program.
This variable named averagePea
is local to the function estTotal.
Variables that are declared within the body of a function definition are said to be local to that function or to have that function as their scope. Variables that are defined within the main
body of the program are said to be local to the main part of the program or to have the main
part of the program as their scope. There are other kinds of variables that are not local to any function or to the main
part of the program, but we will have no use for such variables. Every variable we will use is either local to a function definition or local to the main
part of the program. When we say that a variable is a local variable without any mention of a function and without any mention of the main
part of the program, we mean that the variable is local to some function definition.
The program in Display 4.11 gives an estimate for the total yield on a small garden plot used to raise an experimental variety of peas. The function estTotal
returns an estimate of the total number of peas harvested. The function estTotal
takes three arguments. One argument is the number of pea pods that were harvested. The other two arguments are used to estimate the average number of peas in a pod. Different pea pods contain differing numbers of peas, so the other two arguments to the function are the smallest and the largest number of peas that were found in any one pod. The function estTotal
averages these two numbers and uses this average as an estimate for the average number of peas in a pod.
As we noted in Chapter 2, you can and should name constant values using the const
modifier. For example, in Display 4.10 we used the following declaration to give the name PI
to the constant 3.14159
:
const double PI = 3.14159;
In Display 4.3, we used the const
modifier to give a name to the rate of sales tax with the following declaration:
const double TAX_RATE = 0.05; //5 percent sales tax
As with our variable declarations, we placed these declarations for naming constants inside the body of the functions that used them. This worked out fine because each named constant was used by only one function. However, it can easily happen that more than one function uses a named constant. In that case you can place the declaration for naming a constant at the beginning of your program, outside of the body of all the functions and outside the body of the main
part of your program. The named constant is then said to be a global named constant and the named constant can be used in any function definition that follows the constant declaration.
Display 4.12 shows a program with an example of a global named constant. The program asks for a radius and then computes both the area of a circle and the volume of a sphere with that radius. The programmer who wrote that program looked up the formulas for computing those quantities and found the following:
area = π × (radius)2
volume = (4/3) × π × (radius)3
A Global Named Constant
1 //Computes the area of a circle and the volume of a sphere.
2 //Uses the same radius for both calculations.
3 #include <iostream>
4 #include <cmath>
5 using namespace std;
6
7 const double PI = 3.14159;
8
9 double area (double radius);
10 //Returns the area of a circle with the specified radius.
11
12 double volume(double radius);
13 //Returns the volume of a sphere with the specified radius.
14
15 int main( )
16 {
17 double radiusOfBoth, areaOfCircle, volumeOfSphere;
18
19 cout << "Enter a radius to use for both a circle\n"
20 << "and a sphere (in inches): ";
21 cin >> radiusOfBoth;
22
23 areaOfCircle = area(radiusOfBoth);
24 volumeOfSphere = volume(radiusOfBoth);
25
26 cout << "Radius = " << radiusOfBoth << " inches\n"
27 << "Area of circle = " << areaOfCircle
28 << " square inches\n"
29 << "Volume of sphere = " << volumeOfSphere
30 << " cubic inches\n";
31
32 return 0;
33 }
34
35 double area(double radius)
36 {
37 return (PI * pow(radius, 2));
38 }
39
40 double volume(double radius)
41 {
42 return ((4.0/3.0) * PI * pow(radius, 3));
43 }
Sample Dialogue
Enter a radius to use for both a circle
and a sphere (in inches): 2
Radius = 2 inches
Area of circle = 12.5664 square inches
Volume of sphere = 33.5103 cubic inches
Both formulas include the constant π, which is approximately equal to 3.14159. The symbol π is the Greek letter called “pi.” In previous programs we have used the following declaration to produce a named constant called PI
to use when we convert such formulas to C++ code:
const double PI = 3.14159;
In the program in Display 4.12 we use the same declaration but place it near the beginning of the file so that it defines a global named constant that can be used in all the function bodies.
The compiler allows you wide latitude with regard to where you place the declarations for your global named constants, but to aid readability you should place all your include
directives together, all your global named constant declarations together in another group, and all your function declarations together. We will follow standard practice and place all our global named constant declarations after our include
directives and before our function declarations.
Placing all named constant declarations at the start of your program can aid readability even if the named constant is used by only one function. If the named constant might need to be changed in a future version of your program, it will be easier to find if it is at the beginning of the program. For example, placing the constant declaration for the sales tax rate at the beginning of an accounting program will make it easy to revise the program should the tax rate increase.
It is possible to declare ordinary variables, without the const
modifier, as global variables, which are accessible to all function definitions in the file. This is done the same way that it is done for global named constants, except that the modifier const
is not used in the variable declaration. However, there is seldom any need to use such global variables. Moreover, global variables can make a program harder to understand and maintain, so we will not use any global variables. Once you have had more experience designing programs, you may choose to occasionally use global variables.
Formal parameters are more than just blanks that are filled in with the argument values for the function. Formal parameters are actually variables that are local to the function definition, so they can be used just like a local variable that is declared in the function definition. Earlier in this chapter we described the call-by-value mechanism that handles the arguments in a function call. We can now define this mechanism for “plugging in arguments” in more detail. When a function is called, the formal parameters for the function (which are local variables) are initialized to the values of the arguments. This is the precise meaning of the phrase “plugged in for the formal parameters” that we have been using. Typically, a formal parameter is used only as a kind of blank, or place holder, that is filled in by the value of its corresponding argument; occasionally, however, a formal parameter is used as a variable whose value is changed. In this section we will give one example of a formal parameter used as a local variable.
The program in Display 4.13 is the billing program for the law offices of Dewey, Cheatham, and Howe. Notice that, unlike other law firms, the firm of Dewey, Cheatham, and Howe does not charge for any time less than a quarter of an hour. That is why it’s called “the law office with a heart.” If they work for 1 hour and 14 minutes, they only charge for 4 quarter hours, not 5 quarter hours as other firms do; so you would pay only $600 for the consultation.
Formal Parameter Used as a Local Variable
Sample Dialogue
Welcome to the offices of
Dewey, Cheatham, and Howe.
The law office with a heart.
Enter the hours and minutes of your consultation:
2 45
For 2 hours and 45 minutes, your bill is $1650.00
Notice the formal parameter minutesWorked
in the definition of the function fee
. It is used as a variable and has its value changed by the following line, which occurs within the function definition:
minutesWorked = hoursWorked * 60 + minutesWorked;
Formal parameters are local variables just like the variables you declare within the body of a function. However, you should not add a variable declaration for the formal parameters. Listing the formal parameter minutes_worked
in the function declaration also serves as the variable declaration. The following is the wrong way to start the function definition for fee
as it declares minutesWorked
twice:
The scope of a local variable refers to the part of a program that can directly access that variable and is sometimes referred to as local scope. Similarly, global identifiers declared at the beginning of your program, outside of the body of all the functions, are sometimes referred to as having global scope. Despite their differences, local and global identifiers are really examples of block scope described in Chapter 3. A block is some C++ code enclosed in braces, with the exception of the “global block,” which is an implied outermost block that encompasses all code. The scope rule states that identifiers declared within their block are local to that block and accessible only from the point they are defined to the end of their block. Blocks are commonly nested. For example, the braces of the main
function defines a block and a for
loop inside main
defines a nested block.
The program outlined in Display 4.14 doesn’t compute anything interesting but illustrates the scope of identifiers declared in different blocks. In this example, the constant GLOBAL_CONST
has global scope, along with the functions function1
and main
, because they are declared outside the body of all functions. This allows us to access GLOBAL_CONST
from both main
and function1
.
Block Scope Revisited
The main
function declares the variables x
and d
that are local to main
. Their scope extends to the end of main
’s block. Similarly, the function function1
has a parameter param
and a local variable y
that have scope extending to the end of function1
. Neither of these variables is directly accessible from outside their scope. The scope of local variables and parameters really uses the same rule of block scope, but in this case the block refers to the function in which the variables or parameters are declared.
The for loop in Display 4.14 illustrates the scope of a nested block. The variable i
is declared inside the for loop and thus only has scope to the end of the loop block. Attempts to reference i
anywhere outside its scope, even if we are still inside main
(for example, on line 17) would result in a compiler error.
You can think of variables as being created when their scope begins and destroyed when their scope ends. For example, the local variable y
in Display 4.14 is created and initialized to GLOBAL_CONST
every time function1
is called. If code on line 23 changed the value stored in y
, then these changes would be lost when the function exits and y
goes out of scope because the variable y is destroyed. A repeat call to function1
will not recall the previous value of y
, but rather a new y
will be created.
In addition to block scope there is also namespace scope and class scope. Class scope is discussed in Chapter 10 and namespace scope in Chapter 12. C++ also defines function prototype scope, which refers to the line of scope for parameters defined in a function prototype. Finally, C++ supports function scope, which is used for labels. Labels are a remnant from the C language and are used with goto statements. Their use is generally shunned because they can result in logic that is difficult to follow, whereas the same task can be performed by loops in an understandable fashion.
Thus far, we have started all of our programs with the following two lines:
#include <iostream>
using namespace std;
However, the start of the file is not always the best location for the line
using namespace std;
We will eventually be using more namespaces than just std
. In fact, we may be using different namespaces in different function definitions. If you place the directive
using namespace std;
inside the brace {
that starts the body of a function definition, then the using
directive applies to only that function definition. This will allow you to use two different namespaces in two different function definitions, even if the two function definitions are in the same file and even if the two namespaces have some name(s) with different meanings in the two different namespaces.
Placing a using
directive inside a function definition is analogous to placing a variable declaration inside a function definition. If you place a variable definition inside a function definition, the variable is local to the function; that is, the meaning of the variable declaration is confined to the function definition. If you place a using
directive inside a function definition, the using
directive is local to the function definition; in other words, the meaning of the using
directive is confined to the function definition.
It will be some time before we use any namespace other than std
in a using
directive, but it will be good practice to start placing these using
directives where they should go. In Display 4.15 we have rewritten the program in Display 4.12 with the using
directives where they should be placed. The program in Display 4.15 will behave exactly the same as the one in Display 4.12. In this particular case, the difference is only one of style, but when you start to use more namespaces, the difference will affect how your programs perform.
Using Namespaces
1 //Computes the area of a circle and the volume of a sphere.
2 //Uses the same radius for both calculations.
3 #include <iostream>
4 #include <cmath>
5
6 const double PI = 3.14159;
7
8 double area(double radius);
9 //Returns the area of a circle with the specified radius.
10
11 double volume(double radius);
12 //Returns the volume of a sphere with the specified radius.
13
14 int main( )
15 {
16 using namespace std;
17
18 double radiusOfBoth, areaOfCircle, volumeOfSphere;
19
20 cout << "Enter a radius to use for both a circle\n"
21 << "and a sphere (in inches): ";
22 cin >> radiusOfBoth;
23
24 areaOfCircle = area(radiusOfBoth);
25 volumeOfSphere = volume(radiusOfBoth);
26
27 cout << "Radius = " << radiusOfBoth << " inches\n"
28 << "Area of circle = " << areaOfCircle
29 << " square inches\n"
30 << "Volume of sphere = " << volumeOfSphere
31 << " cubic inches\n";
32
33 return 0;
34 }
35
36
37 double area(double radius)
38 {
39 using namespace std;
40
41 return (PI * pow(radius, 2));
42 }
43
44 double volume(double radius)
45 {
46 using namespace std;
47
48 return ((4.0/3.0) * PI * pow(radius, 3));
49 }
The sample dialogue for this program would be the same as the one for the program in Display 4.12.
If you use a variable in a function definition, where should you declare the variable? In the function definition? In the main
part of the program? Any place that is convenient?
Suppose a function named Function1
has a variable named sam
declared within the definition of Function1
, and a function named Function2
also has a variable named sam
declared within the definition of Function2
. Will the program compile (assuming everything else is correct)? If the program will compile, will it run (assuming that everything else is correct)? If it runs, will it generate an error message when run (assuming everything else is correct)? If it runs and does not produce an error message when run, will it give the correct output (assuming everything else is correct)?
The following function is supposed to take as arguments a length expressed in feet and inches and return the total number of inches in that many feet and inches. For example, total_inches(1,2)
is supposed to return 14
, because 1 foot and 2 inches is the same as 14 inches. Will the following function perform correctly? If not, why not?
double total_inches(int feet, int inches)
{
inches = 12 * feet + inches;
return inches;
}
Write a function declaration and function definition for a function called readFilter
that has no parameters and that returns a value of type double
. The function readFilter
prompts the user for a value of type double
and reads the value into a local variable. The function returns the value read provided this value is greater than or equal to zero and returns zero if the value read is negative.
Display 4.16 contains the function declaration and definition for a commonly used mathematical function known as the factorial function. In mathematics texts, the factorial function is usually written n! and is defined to be the product of all the integers from 1 to n. In traditional mathematical notation, you can define n! as follows:
n! = 1 × 2 × 3 × ... ×
n
In the function definition we perform the multiplication with a while
loop. Note that the multiplication is performed in the reverse order to what you might expect. The program multiplies by n
, then n – 1
, then n – 2
, and so forth.
The function definition for factorial
uses two local variables: product
, which is declared at the start of the function body, and the formal parameter n
. Since a formal parameter is a local variable, we can change its value. In this case we change the value of the formal parameter n
with the decrement operator n––
. (The decrement operator was discussed in Chapter 2.)
Each time the body of the loop is executed, the value of the variable product
is multiplied by the value of n
, and then the value of n
is decreased by one using n––
. If the function factorial
is called with 3
as its argument, then the first time the loop body is executed the value of product
is 3
, the next time the loop body is executed the value of product
is 3 * 2
, the next time the value of product
is 3 * 2 * 1
, and then the while
loop ends. Thus, the following will set the variable x
equal to 6
which is 3 * 2 * 1
:
x = factorial(3);
Notice that the local variable product
is initialized to the value 1
when the variable is declared. (This way of initializing a variable when it is declared was introduced in Chapter 2.) It is easy to see that 1
is the correct initial value for the variable product
. To see that this is the correct initial value for product
, note that after executing the body of the while
loop the first time, we want the value of product
to be equal to the (original) value of the formal parameter n
; if product
is initialized to 1
, then this will be what happens.