|

When
you call a function, execution begins with the first
statement after the opening brace ({). Branching
can be accomplished by using the if statement (and
related statements that will be discussed in unit
7, “More Program Flow”). Functions can also call
other functions and can even call themselves (see
the section “Recursion,” later in this unit).
Local
Variables
Not only can you pass in variables to the function,
but you also can declare variables within the body
of the function. This is done using local variables,
so named because they exist only locally within
the function itself. When the function returns,
the local variables are no longer available.
Local variables are defined like any other variables.
The parameters passed in to the function are also
considered local variables and can be used exactly
as if they had been defined within the body of the
function. Listing 5.2 is an example of using parameters
and locally defined variables within a function.
Listing 5.2. The use of local variables and
parameters.
1:
#include <iostream.h>
2:
3: float Convert(float);
4: int main()
5: {
6: float TempFer;
7: float TempCel;
8:
9: cout << “Please enter the temperature in
Fahrenheit: “;
10: cin >> TempFer;
11: TempCel = Convert(TempFer);
12: cout << “\nHere’s the temperature in Celsius:
“;
13: cout << TempCel << endl;
14: return 0;
15: }
16:
17: float Convert(float TempFer)
18: {
19: float TempCel;
20: TempCel = ((TempFer - 32) * 5) / 9;
21: return TempCel;
22: }
Output:
Please enter the temperature in Fahrenheit: 212
Here’s the temperature in Celsius: 100
Please enter the temperature in Fahrenheit: 32
Here’s the temperature in Celsius: 0
Please enter the temperature in Fahrenheit: 85
Here’s the temperature in Celsius: 29.4444
Analysis: On lines 6 and 7, two float
variables are declared, one to hold the temperature
in Fahrenheit and one to hold the temperature in
degrees Celsius. The user is prompted to enter a
Fahrenheit temperature on line 9, and that value
is passed to the function Convert().
Execution jumps to the first line of the function
Convert() on line 19, where a local variable, also
named TempCel, is declared. Note that this local
variable is not the same as the variable TempCel
on line 7. This variable exists only within the
function Convert(). The value passed as a parameter,
TempFer, is also just a local copy of the variable
passed in by main().
This function could have named the parameter FerTemp
and the local variable CelTemp, and the program
would work equally well. You can enter these names
again and recompile the program to see this work.
The local function variable TempCel is assigned
the value that results from subtracting 32 from
the parameter TempFer, multiplying by 5, and then
dividing by 9. This value is then returned as the
return value of the function, and on line 11 it
is assigned to the variable TempCel in the main()
function.
The
value is printed on line 13.
The program is run three times. The first time,
the value 212 is passed in to ensure that the boiling
point of water in degrees Fahrenheit (212) generates
the correct answer in degrees Celsius (100). The
second test is the freezing point of water. The
third test is a random number chosen to generate
a fractional result.
As an exercise, try entering the program again with
other variable names as illustrated here:
1:
#include <iostream.h>
2:
3: float Convert(float);
4: int main()
5: {
6: float TempFer;
7: float TempCel;
8:
9: cout << “Please enter the temperature in
Fahrenheit: “;
10: cin >> TempFer;
11: TempCel = Convert(TempFer);
12: cout << “\nHere’s the temperature in Celsius:
“;
13: cout << TempCel << endl;
14: }
15:
16: float Convert(float Fer)
17: {
18: float Cel;
19: Cel = ((Fer - 32) * 5) / 9;
20: return Cel;
21: }
You should get the same results.
New Term: A variable
has scope, which determines how long it is available
to your program and where it can be accessed. Variables
declared within a block are scoped to that block;
they can be accessed only within that block and
“go out of existence” when that block ends. Global
variables have global scope and are available anywhere
within your program.
Normally scope is obvious, but there are some tricky
exceptions. Currently, variables declared within
the header of a for loop (for int i = 0; i<SomeValue;
i++) are scoped to the block in which the for loop
is created, but there is talk of changing this in
the official C++ standard.
None of this matters very much if you are careful
not to reuse your variable names within any given
function.
Global Variables
Variables defined outside of any function have global
scope and thus are available from any function in
the program, including main().
Local variables with the same name as global variables
do not change the global variables. A local variable
with the same name as a global variable hides the
global variable, however. If a function has a variable
with the same name as a global variable, the name
refers to the local variable—not the global—when
used within the function. Listing 5.3 illustrates
these points.
Listing 5.3. Demonstrating global and local
variables.
1: #include <iostream.h>
2: void myFunction(); // prototype
3:
4: int x = 5, y = 7; // global variables
5: int main()
6: {
7:
8: cout << “x from main: “ << x <<
“\n”;
9: cout << “y from main: “ << y <<
“\n\n”;
10: myFunction();
11: cout << “Back from myFunction!\n\n”;
12: cout << “x from main: “ << x <<
“\n”;
13: cout << “y from main: “ << y <<
“\n”;
14: return 0;
15: }
16:
17: void myFunction()
18: {
19: int y = 10;
20:
21: cout << “x from myFunction: “ <<
x << “\n”;
22: cout << “y from myFunction: “ <<
y << “\n\n”;
23: }
Output:
x from main: 5
y from main: 7
x from myFunction: 5
y from myFunction: 10
Back from myFunction!
x from main: 5
y from main: 7
Analysis: This simple program illustrates
a few key, and potentially confusing, points about
local and global variables. On line 1, two global
variables, x and y, are declared. The global variable
x is initialized with the value 5, and the global
variable y is initialized with the value 7.
On lines 8 and 9 in the function main(), these values
are printed to the screen. Note that the function
main() defines neither variable; because they are
global, they are already available to main().
When myFunction() is called on line 10, program
execution passes to line 18, and a local variable,
y, is defined and initialized with the value 10.
On line 21, myFunction() prints the value of the
variable x, and the global variable x is used, just
as it was in main(). On line 22, however, when the
variable name y is used, the local variable y is
used, hiding the global variable with the same name.
The function call ends, and control returns to main(),
which again prints the values in the global variables.
Note that the global variable y was totally unaffected
by the value assigned to myFunction()’s local y
variable.
Global Variables: A Word of Caution
In
C++, global variables are legal, but they are almost
never used. C++ grew out of C, and in C global variables
are a dangerous but necessary tool. They are necessary
because there are times when the programmer needs
to make data available to many functions and he
does not want to pass that data as a parameter from
function to function.
Globals are dangerous because they are shared data,
and one function can change a global variable in
a way that is invisible to another function. This
can and does create bugs that are very difficult
to find.
More on Local Variables
Variables declared within the function are said
to have “local scope.” That means, as discussed,
that they are visible and usable only within the
function in which they are defined. In fact, in
C++ you can define variables anywhere within the
function, not just at its top. The scope of the
variable is the block in which it is defined. Thus,
if you define a variable inside a set of braces
within the function, that variable is available
only within that block. Listing 5.4 illustrates
this idea.
Listing 5.4. Variables scoped within a block.
1: // Listing 5.4 - demonstrates variables
2: // scoped within a block
3:
4: #include <iostream.h>
5:
6: void myFunc();
7:
8: int main()
9: {
10: int x = 5;
11: cout << “\nIn main x is: “ << x;
12:
13: myFunc();
14:
15: cout << “\nBack in main, x is: “ <<
x;
16: return 0;
17: }
18:
19: void myFunc()
20: {
21:
22: int x = 8;
23: cout << “\nIn myFunc, local x: “ <<
x << endl;
24:
25: {
26: cout << “\nIn block in myFunc, x is: “
<< x;
27:
28: int x = 9;
29:
30: cout << “\nVery local x: “ << x;
31: }
32:
33: cout << “\nOut of block, in myFunc, x:
“ << x << endl;
34: }
Output:
In main x is: 5
In myFunc, local x: 8
In block in myFunc, x is: 8
Very local x: 9
Out of block, in myFunc, x: 8
Back in main, x is: 5
Analysis: This program begins with the
initialization of a local variable, x, on line 10,
in main(). The printout on line 11 verifies that
x was initialized with the value 5. MyFunc() is
called, and a local variable, also named x, is initialized
with the value 8 on line 22. Its value is printed
on line 23.
A block is started on line 25, and the variable
x from the function is printed again on line 26.
A new variable also named x, but local to the block,
is created on line 28 and initialized with the value
9.
The value of the newest variable x is printed on
line 30. The local block ends on line 31, and the
variable created on line 28 goes “out of scope”
and is no longer visible.
When x is printed on line 33, it is the x that was
declared on line 22. This x was unaffected by the
x that was defined on line 28; its value is still
8.
On line 34, MyFunc() goes out of scope, and its
local variable x becomes unavailable. Execution
returns to line 15, and the value of the local variable
x, which was created on line 10, is printed. It
was unaffected by either of the variables defined
in MyFunc().
Needless to say, this program would be far less
confusing if these three variables were given unique
names!
Function Statements
There is virtually no limit to the number or
types of statements that can be in a function body.
Although you can’t define another function from
within a function, you can call a function, and
of course main() does just that in nearly every
C++ program. Functions can even call themselves,
which is discussed soon, in the section on recursion.
Although there is no limit to the size of a function
in C++, well-designed functions tend to be small.
Many programmers advise keeping your functions short
enough to fit on a single screen so that you can
see the entire function at one time. This is a rule
of thumb, often broken by very good programmers,
but a smaller function is easier to understand and
maintain.
Each function should carry out a single, easily
understood task. If your functions start getting
large, look for places where you can divide them
into component tasks.
Function Arguments
Function arguments do not have to all be of the
same type. It is perfectly reasonable to write a
function that takes an integer, two longs, and a
character as its arguments.
Any valid C++ expression can be a function argument,
including constants, mathematical and logical expressions,
and other functions that return a value.
Using Functions as Parameters to Functions
Although it is legal for one function to take
as a parameter a second function that returns a
value, it can make for code that is hard to read
and hard to debug.
As an example, say you have the functions double(),
triple(), square(), and cube(), each of which returns
a value. You could write
Answer
= (double(triple(square(cube(myValue)))));
This statement takes a variable, myValue, and passes
it as an argument to the function cube(), whose
return value is passed as an argument to the function
square(), whose return value is in turn passed to
triple(), and that return value is passed to double().
The return value of this doubled, tripled, squared,
and cubed number is now passed to Answer.
It is difficult to be certain what this code does
(was the value tripled before or after it was squared?),
and if the answer is wrong it will be hard to figure
out which function failed.
An alternative is to assign each step to its own
intermediate variable:
unsigned long myValue = 2;
unsigned long cubed = cube(myValue); // cubed =
8
unsigned long squared = square(cubed); // squared
= 64
unsigned long tripled = triple(squared); // tripled
= 196
unsigned long Answer = double(tripled); // Answer
= 392
Now each intermediate result can be examined, and
the order of execution is explicit.
Parameters Are Local Variables
The arguments passed in to the function are
local to the function. Changes made to the arguments
do not affect the values in the calling function.
This is known as passing by value, which means a
local copy of each argument is made in the function.
These local copies are treated just like any other
local variables. Listing 5.5 illustrates this point.
Listing 5.5. A demonstration of passing by
value.
1: // Listing 5.5 - demonstrates
passing by value
2:
3: #include <iostream.h>
4:
5: void swap(int x, int y);
6:
7: int main()
8: {
9: int x = 5, y = 10;
10:
11: cout << “Main. Before swap, x: “ <<
x << “ y: “ << y << “\n”;
12: swap(x,y);
13: cout << “Main. After swap, x: “ <<
x << “ y: “ << y << “\n”;
14: return 0;
15: }
16:
17: void swap (int x, int y)
18: {
19: int temp;
20:
21: cout << “Swap. Before swap, x: “ <<
x << “ y: “ << y << “\n”;
22:
23: temp = x;
24: x = y;
25: y = temp;
26:
27: cout << “Swap. After swap, x: “ <<
x << “ y: “ << y << “\n”;
28:
29: }
Output:
Main. Before swap, x: 5 y: 10
Swap. Before swap, x: 5 y: 10
Swap. After swap, x: 10 y: 5
Main. After swap, x: 5 y: 10
Analysis: This program initializes
two variables in main() and then passes them to
the swap() function, which appears to swap them.
When they are examined again in main(), however,
they are unchanged!
The variables are initialized on line 9, and their
values are displayed on line 11. swap() is called,
and the variables are passed in.
Execution of the program switches to the swap()
function, where on line 21 the values are printed
again. They are in the same order as they were in
main(), as expected. On lines 23 to 25 the values
are swapped, and this action is confirmed by the
printout on line 27. Indeed, while in the swap()
function, the values are swapped.
Execution then returns to line 13, back in main(),
where the values are no longer swapped.
As you’ve figured out, the values passed in to the
swap() function are passed by value, meaning that
copies of the values are made that are local to
swap(). These local variables are swapped in lines
23 to 25, but the variables back in main() are unaffected.
In Chapters 8 and 10 you’ll see alternatives to
passing by value that will allow the values in main()
to be changed.
Return Values
Functions return a value or return void. Void
is a signal to the compiler that no value will be
returned.
To return a value from a function, write the keyword
return followed by the value you want to return.
The value might itself be an expression that returns
a value. For example:
return 5;
return (x > 5);
return (MyFunction());
These are all legal return statements, assuming
that the function MyFunction() itself returns a
value. The value in the second statement, return
(x > 5), will be zero if x is not greater than
5, or it will be 1. What is returned is the value
of the expression, 0 (false) or 1 (true), not the
value of x.
When the return keyword is encountered, the expression
following return is returned as the value of the
function. Program execution returns immediately
to the calling function, and any statements following
the return are not executed.
It is legal to have more than one return statement
in a single function. Listing 5.6 illustrates this
idea.
Listing 5.6. A demonstration of multiple
return statements.
1:
// Listing 5.6 - demonstrates multiple return
2: // statements
3:
4: #include <iostream.h>
5:
6: int Doubler(int AmountToDouble);
7:
8: int main()
9: {
10:
11: int result = 0;
12: int input;
13:
14: cout << “Enter a number between 0 and
10,000 to double: “;
15: cin >> input;
16:
17: cout << “\nBefore doubler is called...
“;
18: cout << “\ninput: “ << input <<
“ doubled: “ << result << “\n”;
19:
20: result = Doubler(input);
21:
22: cout << “\nBack from Doubler...\n”;
23: cout << “\ninput: “ << input <<
“ doubled: “ << result << “\n”;
24:
25:
26: return 0;
27: }
28:
29: int Doubler(int original)
30: {
31: if (original <= 10000)
32: return original * 2;
33: else
34: return -1;
35: cout << “You can’t get here!\n”;
36: }
Output:
Enter a number between 0 and 10,000 to double: 9000
Before doubler is called...
input: 9000 doubled: 0
Back from doubler...
input: 9000 doubled: 18000
Enter a number between 0 and 10,000 to double: 11000
Before doubler is called...
input: 11000 doubled: 0
Back from doubler...
input: 11000 doubled: -1
Analysis: A number is requested on lines
14 and 15, and printed on line 18, along with the
local variable result. The function Doubler() is
called on line 20, and the input value is passed
as a parameter. The result will be assigned to the
local variable result, and the values will be reprinted
on lines 22 and 23. On line 31, in the function
Doubler(), the parameter is tested to see whether
it is greater than 10,000. If it is not, the function
returns twice the original number. If it is greater
than 10,000, the function returns -1 as an error
value.
The statement on line 35 is never reached, because
whether or not the value is greater than 10,000,
the function returns before it gets to line 35,
on either line 32 or line 34. A good compiler will
warn that this statement cannot be executed, and
a good programmer will take it out!
Default Parameters
For every parameter you declare in a function
prototype and definition, the calling function must
pass in a value. The value passed in must be of
the declared type. Thus, if you have a function
declared as
long
myFunction(int);
the
function must in fact take an integer variable.
If the function definition differs, or if you fail
to pass in an integer, you will get a compiler error.
The one exception to this rule is if the function
prototype declares a default value for the parameter.
A default value is a value to use if none is supplied.
The preceding declaration could be rewritten as
long
myFunction (int x = 50);
This
prototype says, “myFunction() returns a long and
takes an integer parameter. If an argument is not
supplied, use the default value of 50.” Because
parameter names are not required in function prototypes,
this declaration could have been written as
long
myFunction (int = 50);
The
function definition is not changed by declaring
a default parameter. The function definition header
for this function would be
long
myFunction (int x)
If
the calling function did not include a parameter,
the compiler would fill x with the default value
of 50. The name of the default parameter in the
prototype need not be the same as the name in the
function header; the default value is assigned by
position, not name.
Any or all of the function’s parameters can be assigned
default values. The one restriction is this: If
any of the parameters does not have a default value,
no previous parameter may have a default value.
If the function prototype looks like
long
myFunction (int Param1, int Param2, int Param3);
you
can assign a default value to Param2 only if you
have assigned a default value to Param3. You can
assign a default value to Param1 only if you’ve
assigned default values to both Param2 and Param3.
Listing 5.7 demonstrates the use of default values.
Listing
5.7. A demonstration of default parameter
values.
1: // Listing 5.7 - demonstrates use
2: // of default parameter values
3:
4: #include <iostream.h>
5:
6: int AreaCube(int length, int width = 25, int
height = 1);
7:
8: int main()
9: {
10: int length = 100;
11: int width = 50;
12: int height = 2;
13: int area;
14:
15: area = AreaCube(length, width, height);
16: cout << “First area equals: “ <<
area << “\n”;
17:
18: area = AreaCube(length, width);
19: cout << “Second time area equals: “ <<
area << “\n”;
20:
21: area = AreaCube(length);
22: cout << “Third time area equals: “ <<
area << “\n”;
23: return 0;
24: }
25:
26: AreaCube(int length, int width, int height)
27: {
28:
29: return (length * width * height);
30: }
Output:
First area equals: 10000
Second time area equals: 5000
Third time area equals: 2500
Analysis: On line 6, the AreaCube() prototype
specifies that the AreaCube() function takes three
integer parameters. The last two have default values.
This function computes the area of the cube whose
dimensions are passed in. If no width is passed
in, a width of 25 is used and a height of 1 is used.
If the width but not the height is passed in, a
height of 1 is used. It is not possible to pass
in the height without passing in a width.
On lines 10-12, the dimensions length, height, and
width are initialized, and they are passed to the
AreaCube() function on line 15. The values are computed,
and the result is printed on line 16.
Execution returns to line 18, where AreaCube() is
called again, but with no value for height. The
default value is used, and again the dimensions
are computed and printed.
Execution returns to line 21, and this time neither
the width nor the height is passed in. Execution
branches for a third time to line 27. The default
values are used. The area is computed and then printed.
DO remember that function parameters act as local
variables within the function. DON’T try to create
a default value for a first parameter if there is
no default value for the second. DON’T forget that
arguments passed by value can not affect the variables
in the calling function. DON’T forget that changes
to a global variable in one function change that
variable for all functions.
|