Functions without Arguments

advertisement
Functions
Functions without Arguments
One way a programmer will use top-down design is by
creating functions. The programmer will often write on
function for each of the subproblems in the structure
chart. In this section will focus on simple function design
(without arguments and return values).
A structure chart for drawing a house might look like this:
Draw House
Draw Triangle
Draw Intersecting Lines
Draw Parallel
Lines
Draw Base
Line
Draw Base
Line
In this example, we could design four functions to handle the
subproblems or parts of the structure chart. We could use the
main function to actually draw the house by calling each of the
functions at the appropriate time.
Functions
// Draws a house (main function only)
#include <iostream>
using namespace std;
// Functions used . . .
void drawTriangle();
// Draws a triangle
void drawIntersect();
// Draws intersecting lines
void drawBase();
// Draws a horizontal line
void drawParallel();
// Draws parallel lines
int main()
{
// Draw a triangle
drawTriangle();
// Draw parallel lines
drawParallel();
// Draw a horizontal line
drawBase();
return (0);
}
Inside main() there are three function calls, that pass no
arguments and return no values. Each call activates the function
named, causing it to be executed. When execution completes,
control is passed back to main() and the next line in main() is
executed.
Functions
Function Prototypes
Just like any other identifier in C++, a function must be declared
before it can be used. Inserting a function prototype before the
main() function is one way to declare a function. The prototype
gives the compiler information about the function:
 the data type of the function
 the function name
 the data types and number of arguments.
The data type of the function is determined by the data type of
the value that will be returned by the function. When a function
returns no value, we use the reserved word, void, as its data
type:
void drawTriangle();
The empty parentheses after the function name mean that the
function accepts no arguments.
Note: Function prototypes must always end in a semicolon.
They are just declarations and do not define the actual
function.
Functions
Function Definitions
The prototype is used to provide the compiler with some basic
information about the function so that the programmer’s use of
the function call can be verified for accuracy. It does NOT
provide the operation detail of the function. You need to define
a function body for each of the subproblems in the same way
that you define the main() function.
To define a function, you need a function header. The header
is similar to the prototype, except that there is no semicolon.
Instead of the semicolon, you provide a function body enclosed
in a set of braces {}.
We could define the function to draw parallel lines, as follows:
// Draws parallel lines
void drawParallel()
{
cout << “ |
| ” << endl;
cout << “ |
| ” << endl;
cout << “ |
| ” << endl;
return;
}
Since this function is of type void, the return statement is not
necessary. However, it is a good programming practice to
include one anyway.
Functions
Each function body could be considered a self-contained miniprogram. It can contain its own variable declarations. Those
variables declared inside a function body are considered to be
local to that function and can be accessed only from within that
function. (More later)
In-Class Exercise:
1.
Write a function that draws three blank lines.
Answer: [
void drawBlanks ( )
{
cout << endl << endl << endl;
return;
}
]
Placement of Functions and Prototypes
The function prototypes for the functions to be used in a
program are normally placed above the main function after any
#include directives. The order of the prototypes and the
function definitions does not affect their order of execution.
The order of execution is determined by the order of the
function call statements.
Functions
Program Example:
// File: house.cpp
// Draws a house
#include <iostream>
using namespace std;
// Functions used . . .
void drawTriangle();
void drawIntersect();
void drawBase();
void drawParallel();
// Draws a triangle
// Draws intersecting lines
// Draws a horizontal line
// Draws parallel lines
int main()
{
// Draw a triangle
drawTriangle();
// Draw parallel lines
drawParallel();
// Draw a horizontal line
drawBase();
return (0);
}
// Draws parallel lines
void drawParallel()
{
cout << “ |
| ” << endl;
cout << “ |
| ” << endl;
cout << “ |
| ” << endl;
Functions
return;
} // end drawParallel
// Draws a triangle
void drawTriangle()
{
drawIntersect();
drawBase();
return;
} // end drawTriangle
// Draws intersecting lines
void drawIntersect()
{
cout << “
/\\
“ << endl;
cout << “ / \\ “ << endl;
cout << “ /
\\ “ << endl;
return;
} // end drawIntersect
// Draws a horizontal line
void drawBase()
{
cout << “ ------- “ << endl;
return;
} // end drawBase
NOTE: Notice the use of comments. Documenting your code
thoroughly is a good programming practice.
Functions
Order of Execution of Functions
Since the function prototypes appear before the main function,
the compiler sees and processes them before it sees the main
function. The information supplied by the prototype enables the
compiler to correctly translate a call to that function, as a
transfer of control to that function. It also enables the compiler
to verify the syntax of that call to be sure it matches the
parameters set by the prototype.
After the main function is compiled, each of the function
definitions are then compiled. As part of the translation process,
the compiler inserts a machine language statement at the end of
the function body, that causes a transfer of control back from the
function to the calling statement.
Each function executes in a separate area of the memory
allocated to run the program. When a function is called an area
of memory is opened in which the function will execute. When
the function completes, control is passed back to the calling
statement and the memory is released.
Advantages of Using Functions
There are many advantages to using functions:
 Aid in program organization (modularity)
 Easier for programming teams
 Simplify programming tasks since existing functions can be
reused
 Allows the removal of the details from main()
o Procedural Abstraction
 Can be executed more than once in the same program
Functions
Functions of type void which accept no arguments have a very
limited capability. Without incoming or outgoing data, these
functions can only be used to display information. They are
useful for displaying instructions, error messages and the like.
In-Class Exercise:
1. Write a program that prompts the user for his first name,
last name, student ID, and email address.
2. Using the function you wrote earlier, print three blank
lines, followed by the user’s information displayed in the
following format:
Name: Mary Smith
Student ID: 123-45-6789
Email Address: mxk10@psu.edu
Functions
Function with Input Arguments (Section 3.5)
So far the only functions we have had to call have been the
mathematical functions. In this chapter we will start to write our
own functions. In creating C++ functions we must be concerned
with both the function itself and how it interfaces with other
functions, such as main( ). This includes correctly passing data
into a function when it is called and returning a value back from
a function.
Arguments that pass information into the function are called
input arguments. Arguments that return results are called
output arguments. We can also return a single result from a
function by executing a return statement. The use of arguments
make functions much more versatile because it enables the
function to manipulate different data each time it is called.
As with the mathematical functions, a function is called by
giving the function's name and passing any data to it in the
parentheses following the function name:
function_name (data passed to function);
For example, the following program will call the function
drawCircleChar( ), passing the value stored in input to it.
Functions
#include <iostream>
using namespace std;
int main( )
{
char input;
void drawCircleChar (char); // the function prototype
cout << "\nEnter a single character: ";
cin >> input;
drawCircleChar (input);
return (0);
// the function is called here
}
The function drawCircleChar( ) is referred to as the called
function and the function main( ) is referred to as the calling
function. Within main the function drawCircleChar( ) is
declared to receive one character value and returns no value
back to main( ). This declaration is formally referred to as a
function prototype. The function is then called somewhere in
the program.
Functions
Function Prototypes
Before a function can be called, it must be declared to the
function that will do the calling. The declaration statement for a
function is referred to as a function prototype. The function
prototype will tell the calling function the type of value that will
be formally returned, if any, and the data type of the values that
the calling function should transmit to the called function. For
example, the function prototype previously used:
void drawCircleChar (char);
declares that the function drawCircleChar( ) expects one
character value to be sent to it, and that this particular function
formally returns no value (void). The function prototype may be
placed with the variable declaration statements of the calling
function, or above the calling function name, or after the
statement #include <iostream>. The general form of a function
prototype is:
return_data_type function_name (list of argument data types);
For example:
int fmax (int, int);
float swap (int, char, char, double);
void display (double, double);
Functions
Calling a Function
To call a function, all that is required is that the name of the
function be used and that any data passed to the function be
enclosed within the parentheses following the function name.
For example:
drawCircleChar (input);
Note: The function will only receive the value of input and it
must determine where to store that value before it does
anything else.
Defining a Function
A function is defined when it is written. Each function is
defined once in a program and can then be used by any other
function in the program that suitably declares it.
Like the main( ) function, every C++ function definition
consists of two parts, a function header and a function body:
function header line
{
C++ statements;
}
Functions
For example:
void drawCircleChar (char symbol)
represents a function header. The argument symbol will be used
to store value passed to the function.
void drawCircleChar(char symbol)
{
// start of function body
cout << “ “ << symbol << endl;
cout << “ “ << symbol << “ “ << symbol << endl;
cout << “ “ << symbol << “ “ << symbol << endl;
return;
}
// end of function body and end of function
Putting it all together -- Program 3-7:
#include <iostream>
using namespace std;
int main( )
{
char input;
void drawCircleChar (char); // the function prototype
cout << "\nEnter a single character: ";
cin >> input;
drawCircleChar (input);
called here
return (0);
// the function is
Functions
}
// following is the function drawCircleChar()
void drawCircleChar(char symbol)
{
// start of function body
cout << “ “ << symbol << endl;
cout << “ “ << symbol << “ “ << symbol << endl;
cout << “ “ << symbol << “ “ << symbol << endl;
return;
}
// end of function body and end of function
In-Class Exercise:
I.
Write a function named check( ) that has three
parameters. The first parameter should accept an integer
number, the second parameter a floating-point number,
and the third parameter a double-precision number. The
body of the function should display the values of the data
passed to the function when it is called.
II.
Include the function written above in a working program.
Make sure your function is called from
main( ). Test the function by passing various data to it.
Functions
Argument/Parameter List Correspondence
When you are using multiple-argument functions, you must be
careful to include the correct number and types of arguments in
the function call. The following summarizes the constraints on
the number, order and type (not) of input arguments (as quoted
from your book):
I.
The number of actual arguments used in a call to function
must be the same as the number of formal parameters listed in
the function prototype.
II.
The order of arguments in the lists determines the
correspondence. The first must correspond to the first, the
second to the second, and so on.
III. Each actual argument must be of a data type that can be
assigned to the corresponding formal parameter without loss
of information.
Functions
Default Arguments
A convenient feature of C++ is the flexibility of providing
default arguments in a function call. The default argument
values are listed in the function prototype and are automatically
transmitted to the called function when the corresponding
arguments are omitted from the function call. For example:
void example (int, int =5, float = 6.78)
provides default values for the last two arguments. If any of
these arguments are omitted when the function is actually called,
the C++ compiler will supply these default values. Thus all of
the following function calls are the same:
example (7, 2, 9.3)
example (7, 2)
example (7)
Functions
Three rules must be followed when using default parameters:
I.
The first is that, if any parameter is given a default value in
the function prototype, all parameters following it must also
be supplied with default values.
II.
The second rule is that if one argument is omitted in the
actual function call, then all arguments to its right must also
be omitted.
III. The third rule specifies that the default value used in the
function prototype may be an expression consisting of both
constants and previously declared variables.
The true benefit of default arguments is realized when you want
to extend an existing function to include more features that
require additional arguments. By adding the new arguments to
the right of the previously existing arguments and providing
each with a default value eliminates the need to change all
existing calls to that function. These existing function calls are
conveniently unaffected by your changes.
Functions
Reusing Function Names (Function Overloading)
In most high-level languages, including C, each function must
have its own unique name. This can lead to the necessity to
create multiple functions that perform the same functionality but
accept arguments of a different data type. For example, let’s
look at the following function prototypes:
void abs (int);
void labs (long);
void fabs (double);
Each of these three functions will perform essentially the same
operation but on a different argument data type.
C++ provides the capability of using the same function name for
more than on function, which is referred to as function
overloading. Each function that uses the name must still be
written and exist as a separate entity. The use of the same
function name does not require that the code within the
functions be similar, although good programming practice
dictates that functions with the same name should perform
essentially the same operations. All that is formally required in
using the same function name is that the compiler can
distinguish which function to select based on the data types of
the arguments when the function is called.
Functions
Using function overloading, the prototypes for functions to
compute and display the absolute value of a specific data type
might look as follows:
void cdabs (int);
void cdabs (long);
void cdabs (double);
Returning Values
When a function is called by value it may process the data sent
to it in any fashion desired and directly return at most one, and
only one, "legitimate" value to the calling function.
As with the calling of a function, directly returning a value
requires that the interface between the called and calling
functions be handled correctly. From its side of the return
transaction, the called function must provide the following
items:
1. the data type of the returned value
2. the actual value being returned
A function returning a value must specify the data type of the
value that will be returned in the header line. For example, the
header for a function that calculates and returns the
circumference of a circle, given the value of the radius, might
look as follows:
float findCircum (float r)
Functions
This means that the findCircum() function accepts a single float
value, which is the circle’s radius. It will then calculate the
circumference and return that value as a float. Float is the data
type of the findCircum() function. Moreover, for a function to
return a value it must use a return statement, which will be of
the form:
return expression;
or
return (expression);
When the return statement is encountered, the expression is
evaluated first.
The value of the expression is then
automatically converted to the data type declared in the header
before being sent back to the calling function.
The findCircum() definition could read as follows:
float findCircum (float r)
{
// start of function body
return (2.0 * PI * r);
// PI is a constant
}
// end of function body and end of function
Now, we must prepare the calling function to receive the value
sent by the called function. On the calling side, the function
must:
1. be alerted to the type of value to expect
2. properly use the returned value
Functions
To use a returned value we must either provide a variable to
store the value or use the value directly in an expression.
Storing the returned value in a variable is accomplished using a
standard assignment statement. For example:
circumference = findCircum (radius);
To use the returned value in an expression we can do something
like this:
cout << “The circumference is: “ << findCircum(radius)
<< endl;
This will display the value returned by the function.
In-Class Exercise:
I.
Write a function called findArea() whose prototype
looks like:
float findArea(float);
II. Write a main() function that calls and uses the
findCircum() and your findArea() function after
prompting the user for the circle’s radius. Be sure to define
the PI constant.
Functions
ANOTHER EXAMPLE – Program 3-8:
#include <iostream>
using namespace std;
int main ()
{
double fahren;
// start of declarations
double tempConvert(double); // function prototype
cout << "\nEnter a Fahrenheit temperature: ";
cin >> fahren;
cout << "The Celsius equivalent is "
<< tempConvert(fahren)
<< endl;
return 0;
}
// convert Fahrenheit to Celsius
double tempConvert (double in_temp)
{
return( (5.0 / 9.0) * (in_temp - 32.0) );
}
Functions
In-Class Exercise:
A second-degree polynomial in x is given by the expression
ax2 + bx + c
where a, b, and c are known numbers and a is not equal to zero.
Write a C++ function named poly_two (a, b, c ,x) that computes
and returns the value of a second-degree polynomial for any
passed values of a, b, c, and x.
Problem Inputs vs. Input Parameters
It is a common programming error for the beginner programmer
to confuse the concepts of problem inputs and input parameters.
Problem inputs are variables that are given their data from the
user through the execution of a cin or other input statement.
Input parameters receive their data through the execution of a
function call statement. Therefore, the data being passed to an
input parameter must already be defined before the function is
called. The programmer should not prompt and read the input
parameters inside the body of the function definition.
Functions
The Function Data Area
Each time a function is called, a separate area of memory is
allocated to store that function’s data. The function’s formal
parameters and any local (or auto) variables that are declared in
that function are stored in the function’s data area. When the
function terminates all data in the function’s data area is lost.
The data area is recreated each time the function is called.
Testing Functions Using Drivers
A function is an independent module of programming code. It
can be created by an individual programmer and be tested
independently from the program that uses it. To perform such a
test, a programmer will usually design a short driver function
that defines the function arguments, calls the function and
displays the value returned in order to test the functionality or
operation of the function. Since main( ) is necessary to run any
program, a main( ) function is written as a driver to test any
function.
Functions
Program Example:
// File testScale.cpp
// Tests function scale.
#include <iostream>
#include <cmath>
#include <conio.h>
using namespace std;
float scale(float, int);
// Needed for getch();
// Function prototype
int main()
{
float num1;
int num2;
// Get values for num1 and num2
cout << "Enter a real number: ";
cin >> num1;
cout << "Enter an integer: ";
cin >> num2;
// Call scale and display result.
cout << "Result of call to function scale is "
<< scale(num1, num2) // actual arguments
<< endl;
getch();
return 0;
}
// reads any single character
// information flow
Functions
float scale(float x, int n) // formal parameters
{
float scaleFactor; // local variable
scaleFactor = pow(10, n);
return (x * scaleFactor);
}
Function main
Data Area
Function scale
Data Area
num1
x
2.5
2.5
num2
n
3
2.5
scaleFactor
?
Data areas after call:
scale(num1, num2);
Functions
In-Class Exercise:
Write a function that calculates the elapsed time in minutes
between a start time and an end time, expressed as integers on a
24-hour clock (8:30 PM = 2030). You need to deal only with
end times occurring later on the same day as the start time. Also
write a driver program to test your function.
Variable Scope (Section 3.6)
By their very nature, C++ functions are constructed to be
independent modules. We can think of functions as a closed
box, with slots at the top to receive values and a single slot at the
bottom of the box to return a value. The variables created
inside a function are conventionally available only in the
function itself. They are said to be local to the function, local
variables. This term refers to the scope of a variable. The
scope of a variable is defined as the section of the program
where the variable is valid or "known". A variable can have
either a local scope or a global scope.
Local variables are meaningful only when used in expressions or
statements inside the function that declared them. This means
that the same variable name can be used in more than one
function. For each function that declares the variable, a separate
and distinct variable is created.
Functions
Most of the variables we have used until now have been local
variables. This is a direct result of placing our declaration
statements inside functions.
A variable with global scope, more commonly termed a global
variable, is one whose storage has been created for it by a
declaration statement located outside any function. These
variables can be used by all functions that are physically placed
after the global variable declaration.
Program 3-9:
#include <iostream>
using namespace std;
int firstnum;
// create a global variable named firstnum
int main ( )
{
int secnum; // create a local variable named secnum
void valfun (void); // function prototype (declaration)
firstnum = 10; // store a value into the global variable
secnum = 20; // store a value into the local variable
cout << "\nFrom main(): firstnum = " << firstnum;
cout << "\nFrom main(): secnum = " << secnum;
valfun ( );
// call the function valfun
cout << "\n\nFrom main() again: firstnum = " << firstnum;
Functions
cout << "\nFrom main() again: secnum = " << secnum;
return 0;
}
void valfun(void) // no values are passed to this function
{
int secnum; // create a second local variable named
// secnum
secnum = 30; // this only affects this local variable's value
cout << "\n\nFrom valfun(): firstnum = " << firstnum;
cout << "\nFrom valfun(): secnum = " << secnum << endl;
firstnum = 40;
// this changes firstnum for both
// functions
}
OUTPUT:
From main(): firstnum = 10
From main(): secnum = 20
From valfun(): firstnum = 10
From valfun(): secnum = 30
From main() again: firstnum = 40
From main() again: secnum = 20
Functions
Global Scope Resolution Operator
When a local variable has the same name as a global variable,
all references to the variable name made within the scope of the
local variable refer to the local variable. This means that the
local variable name takes precedence over the global variable.
For example -- Program 3-10:
#include <iostream>
using namespace std;
float number = 42.8f;
// a global variable named number
int main ( )
{
float number = 26.4f; // a local variable named number
cout << "The value of number is " << number << '\n';
return 0;
}
In such cases, we can still access the global variable by using
C++ global resolution operator, which has the symbol ::. It
must be placed immediately before the variable name, as in
::number
When used in this manner :: tells the compiler to use the global
variable.
Functions
For example -- Program 3-11:
#include <iostream>
using namespace std;
float number = 42.8f;
int main ( )
{
float number = 26.4;
// a global variable named number
// a local variable named number
cout << "The value of number is " << ::number << '\n';
return 0;
}
Misuse of Globals
Global variables allow the programmer to "jump around" the
normal safeguard provided by functions. It is possible to make
all variables global ones. DO NOT DO THIS. Using only
global variables can be especially disastrous in larger programs
that have many user-created functions. Since a global variable
can be accessed and changed by any function following the
global declaration, it is time consuming and frustrating task to
locate the origin of an erroneous value.
Global variables, are sometimes useful in creating variables that
must be shared between many functions. Rather than passing
the same variable to each function, it is easier to define the
variable once as a global.
Functions
Variable Storage Class
The scope of a variable defines the location within a program
where that variable can be used. Given a program, you could
take a pencil and draw a box around the section of the program
where each variable is valid. The space inside the box would
represent the scope of a variable.
In addition to the space dimension variables also have a time
dimension. The time dimension refers to the length of time
that storage locations are reserved for a variable. This time
dimension is referred to as the variable's "lifetime." Where and
how long a variable's storage locations are kept before they are
released can be determined by the storage class of the variable.
Besides having a data type and scope, every variable also has a
storage class.
The four available storage classes are called auto, static, extern,
and register. The storage class is a modifier that precedes the
data type when you declare a variable. Examples:
auto int num;
static int miles;
extern float num;
auto char in_key;
Functions
I.
auto (keyword is optional):
This is the default storage class for internal or local
variables. Variables of this type come into existence when
a function is entered. Their storage space is released when
the function exits. This class of variable can be initialized
when declared, but, if it is not initialized, it will contain
whatever value is in the memory location assigned to the
variable.
II.
static:
Storage space for static variables is allocated when the
program is loaded into memory and stay in memory for the
life of the program. Static variables may be initialized at
the time of declaration; otherwise, they are automatically
initialized to zero.
III. extern:
The extern storage class tells the compiler that a variable of
a certain type has already been defined elsewhere in the
program and that the compiler should not allocate memory
space for the variable at this time. The compiler will not
issue warning messages unless it cannot locate the
definition of the variable when all modules are linked.
This is used for global variables.
Functions
IV. register:
The register variables have the same time duration and
scope as auto variables. The only difference is where the
storage is located. Most computers have a few additional
high-speed storage areas located directly in the computer’s
processing unit (CPU ), that can also be used for variable
storage. These areas are called registers. They can be
accessed faster than normal memory and computer
instructions that reference registers typically require less
space. Use of registers can increase the execution speed of
a C++ program, if their use is supported on your computer.
Variables declared with the register storage class are
automatically switched to auto if your computer does not
support register variables or if the declared variables
exceed the computer’s register capacity.
Download