Chapter 5 - Modular Programming
Chapter 5 - Modular Programming
Modular Programming
Modules in C++ are called functions. A function is a subprogram that can act on data and
return a value. Every C++ program has at least one function, m a i n ( ) . When your program
starts, m a i n ( ) is called automatically. m a i n ( ) might call other functions, some of which
might call still others. Each function has its own name, and when that name is encountered,
the execution of the program branches to the body of that function. When the function
returns, execution resumes on the next line of the calling function. When a program calls a
function, execution switches to the function and then resumes at the line after the function
call. Well-designed functions perform a specific and easily understood task. Complicated
tasks should be broken down into multiple functions, and then each can be called in turn.
Functions come in two varieties: user-defined and built-in. Built-in functions are part of
your compiler package--they are supplied by the manufacturer for your use. In this chapter
we will discuss about user-defined functions.
Using functions in your program requires that you first declare the function and that you
then define the function. The declaration tells the compiler the name, return type, and
parameters of the function. The definition tells the compiler how the function works. No
function can be called from any other function that hasn't first been declared. The
declaration of a function is called its prototype.
· Write your prototype into a file, and then use the # i n c l u d e directive to include it in
your program.
· Write the prototype into the file in which your function is used.
· Define the function before it is called by any other function. When you do this, the
definition acts as its own declaration.
Although you can define the function before using it, and thus avoid the necessity of
creating a function prototype, this is not good programming practice for three reasons. First,
it is a bad idea to require that functions appear in a file in a particular order. Doing so
makes it hard to maintain the program as requirements change. Second, it is possible that
function A ( ) needs to be able to call function B ( ) , but function B ( ) also needs to be able to
-2-
call function A ( ) under some circumstances. It is not possible to define function A ( ) before
you define function B ( ) and also to define function B ( ) before you define function A ( ) , so
at least one of them must be declared in any case. Third, function prototypes are a good and
powerful debugging technique. If your prototype declares that your function takes a
particular set of parameters, or that it returns a particular type of value, and then your
function does not match the prototype, the compiler can flag your error instead of waiting
for it to show itself when you run the program.
Function Prototypes
Many of the built-in functions you use have their function prototypes already written in the
files you include in your program by using # i n c l u d e . For functions you write yourself, you
must include the prototype. The function prototype is a statement, which means it ends with
a semicolon. It consists of the function's return type, name, and parameter list. The
parameter list is a list of all the parameters and their types, separated by commas. Function
Prototype Syntax:
The function prototype and the function definition must agree exactly about the return type,
the name, and the parameter list. If they do not agree, you will get a compile-time error.
Note, however, that the function prototype does not need to contain the names of the
parameters, just their types. A prototype that looks like this is perfectly legal:
This prototype declares a function named A r e a ( ) that returns a l o n g and that has two
parameters, both integers. Although this is legal, it is not a good idea. Adding parameter
names makes your prototype clearer. The same function with named parameters might be
It is now obvious what this function does and what the parameters are. Note that all
functions have a return type. If none is explicitly stated, the return type defaults to i n t .
Your programs will be easier to understand, however, if you explicitly declare the return
type of every function, including m a i n ( ) .
Listing 5.1 demonstrates a program that includes a function prototype for the A r e a ( )
function.
-3-
Listing 5.1. A function declaration and the definition and use of that function.
Analysis: The prototype for the F i n d A r e a ( ) function is on line 5. Compare the prototype
with the definition of the function on line 26. Note that the name, the return type, and the
parameter types are the same. If they were different, a compiler error would have been
generated. In fact, the only required difference is that the function prototype ends with a
semicolon and has no body. Also note that the parameter names in the prototype are
l e n g t h and w i d t h , but the parameter names in the definition are l and w . The names in the
prototype are not used; they are there as information to the programmer. When they are
included, they should match the implementation when possible. This is a matter of good
programming style and reduces confusion, but it is not required, as you see here.
-4-
The arguments are passed in to the function in the order in which they are declared and
defined, but there is no matching of the names. Had you passed in w i d t h O f Y a r d , followed
by l e n g t h O f Y a r d , the F i n d A r e a ( ) function would have used the value in w i d t h O f Y a r d
for l e n g t h and l e n g t h O f Y a r d for w i d t h . The body of the function is always enclosed in
braces, even when it consists of only one statement, as in this case.
The definition of a function consists of the function header and its body. The header is
exactly like the function prototype, except that the parameters must be named, and there is
no terminating semicolon. The body of the function is a set of statements enclosed in braces.
Function Definition Syntax
A function prototype tells the compiler the return type, name, and parameter list. Func-tions
are not required to have parameters, and if they do, the prototype is not required to list their
names, only their types. A prototype always ends with a semicolon (;). A function
definition must agree in return type and parameter list with its prototype. It must provide
names for all the parameters, and the body of the function definition must be surrounded by
braces. All statements within the body of the function must be terminated with semicolons,
but the function itself is not ended with a semicolon; it ends with a closing brace. If the
function returns a value, it should end with a r e t u r n statement, although r e t u r n
statements can legally appear anywhere in the body of the function. Every function has a
return type. If one is not explicitly designated, the return type will be i n t . Be sure to give
every function an explicit return type. If a function does not return a value, its return type
will be v o i d .
v o i d P r i n t M e s s a g e ( i n t w h i c h M s g )
{
i f ( w h i c h M s g = = 0 )
c o u t < < " H e l l o . \ n " ;
i f ( w h i c h M s g = = 1 )
c o u t < < " G o o d b y e . \ n " ;
i f ( w h i c h M s g > 1 )
c o u t < < " I ' m c o n f u s e d . \ n " ;
}
Function Statements
-5-
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 m a i n ( ) 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.
Execution of Functions
When you call a function, execution begins with the first statement after the opening brace
({ ). Branching can be accomplished by using the i f statement. Functions can also call
other functions and can even call themselves (see the section "Recursion," later in this
chapter). Each function has its own name, and when that name is encountered, the
execution of the program branches to the body of that function. When the function returns,
execution resumes on the next line of the calling function. This flow is illustrated in the
following figure.
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
-6-
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. Variables declared within the
function are said to have "local scope." That means 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.2 is an example of using
parameters and locally defined variables within a function.
1 : # i n c l u d e < i o s t r e a m . h >
2 :
3 : f l o a t C o n v e r t ( f l o a t ) ;
4 : i n t m a i n ( )
5 : {
6 : f l o a t T e m p F e r ;
7 : f l o a t T e m p C e l ;
8 :
9 : c o u t < < " P l e a s e e n t e r t h e t e m p e r a t u r e i n F a h r e n h e i t : " ;
1 0 : c i n > > T e m p F e r ;
1 1 : T e m p C e l = C o n v e r t ( T e m p F e r ) ;
1 2 : c o u t < < " \ n H e r e ' s t h e t e m p e r a t u r e i n C e l s i u s : " ;
1 3 : c o u t < < T e m p C e l < < e n d l ;
1 4 : r e t u r n 0 ;
1 5 : }
1 6 :
1 7 : f l o a t C o n v e r t ( f l o a t T F e r )
1 8 : {
1 9 : f l o a t T C e l ;
2 0 : T C e l = ( ( T F e r - 3 2 ) * 5 ) / 9 ;
2 1 : r e t u r n T C e l ;
2 2 : }
O u t p u t : P l e a s e e n t e r t h e t e m p e r a t u r e i n F a h r e n h e i t : 2 1 2
H e r e ' s t h e t e m p e r a t u r e i n C e l s i u s : 1 0 0
P l e a s e e n t e r t h e t e m p e r a t u r e i n F a h r e n h e i t : 3 2
H e r e ' s t h e t e m p e r a t u r e i n C e l s i u s : 0
P l e a s e e n t e r t h e t e m p e r a t u r e i n F a h r e n h e i t : 8 5
H e r e ' s t h e t e m p e r a t u r e i n C e l s i u s : 2 9 . 4 4 4 4
Analysis: On lines 6 and 7, two f l o a t 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 C o n v e r t ( ) .
Execution jumps to the first line of the function C o n v e r t ( ) on line 19, where a local
-7-
variable, named T C e l , is declared. Note that this is local variable that exists only within the
function C o n v e r t ( ) . The value passed as a parameter, T F e r , is also just a local copy of the
variable passed in by m a i n ( ) .The local function variable T C e l is assigned the value that
results from subtracting 32 from the parameter T F e r , 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 T e m p C e l in the m a i n ( ) function. The value is printed on line 13.
The program is run three times. The first time, the value 2 1 2 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.
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
f o r loop ( f o r i n t i = 0 ; i < S o m e V a l u e ; i + + ) are scoped to the block in which the f o r
loop is created.
Variables defined outside of any function have global scope and thus are available from
any function in the program, including m a i n ( ) . 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.
-8-
2 2 : c o u t < < " y f r o m m y F u n c t i o n : " < < y < < " \ n \ n " ;
2 3 : }
O u t p u t : x f r o m m a i n : 5
y f r o m m a i n : 7
x f r o m m y F u n c t i o n : 5
y f r o m m y F u n c t i o n : 1 0
B a c k f r o m m y F u n c t i o n !
x f r o m m a i n : 5
y f r o m m a i n : 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 m a i n ( ) , these values are printed to the screen.
Note that the function m a i n ( ) defines neither variable; because they are global, they are
already available to m a i n ( ) .
When m y F u n c t i o n ( ) is called on line 10, program execution passes to line 18, and a local
variable, y , is defined and initialized with the value 1 0 . On line 21, m y F u n c t i o n ( ) prints
the value of the variable x , and the global variable x is used, just as it was in m a i n ( ) . 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 m a i n ( ) ,
which again prints the values in the global variables. Note that the global variable y was
totally unaffected by the value assigned to m y F u n c t i o n ( ) 's local y variable.
-9-
2 2 : i n t x = 8 ;
2 3 : c o u t < < " \ n I n m y F u n c , l o c a l x : " < < x < < e n d l ;
2 4 :
2 5 : {
2 6 : c o u t < < " \ n I n b l o c k i n m y F u n c , x i s : " < < x ;
2 7 :
2 8 : i n t x = 9 ;
2 9 :
3 0 : c o u t < < " \ n V e r y l o c a l x : " < < x ;
3 1 : }
3 2 :
3 3 : c o u t < < " \ n O u t o f b l o c k , i n m y F u n c , x : " < < x < < e n d l ;
3 4 : }
O u t p u t : I n m a i n x i s : 5
I n m y F u n c , l o c a l x : 8
I n b l o c k i n m y F u n c , x i s : 8
V e r y l o c a l x : 9
O u t o f b l o c k , i n m y F u n c , x : 8
B a c k i n m a i n , x i s : 5
Analysis: This program begins with the initialization of a local variable, x , on line 10, in
m a i n ( ) . The printout on line 11 verifies that x was initialized with the value 5 . M y F u n c ( ) 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, M y F u n c ( ) 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 M y F u n c ( ) . Needless to say, this program would be far less
confusing if these three variables were given unique names!
In C++, global variables are legal, but they are almost never used. 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.
When a local variable has the same name as a global variable, all references to the variable
name made with in the scope of the local variable. This situation is illustrated in the
following program.
- 10-
# i n c l u d e < i o s t r e a m . h >
f l o a t n u m = 4 2 . 8 ; / / g l o b a l v a r i a b l e
i n t m a i n ( )
{
f l o a t n u m = 2 6 . 4 ; / / l o c a l v a r i a b l e
c o u t < < " t h e v a l u e o f n u m i s : " < < n u m < < e n d l ;
r e t u r n 0 ;
}
The output of the above program is:
t h e v a l u e o f n u m i s : 2 6 . 4
As shown by the above output, the local variable name takes precedence ovger the global
variable. In such cases, we can still access the global variable by using C++'s scope
resolution operator (::). This operator must be placed immediately before the variable name,
as in ::num. When used in this manner, the :: tells the compiler to use global variable.E.g
f l o a t n u m = 4 2 . 8 ; / / g l o b a l v a r i a b l e
i n t m a i n ( )
{
f l o a t n u m = 2 6 . 4 ; / / l o c a l v a r i a b l e
c o u t < < " t h e v a l u e o f n u m i s : " < < : : n u m < < e n d l ;
r e t u r n 0 ;
}
The out is:
t h e v a l u e o f t h e n u m b e r i s 4 2 . 8
As indicated the above output, the scope resolution operator causes the global, rather the
local variable to be accessed.
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 l o n g s, 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.
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 d o u b l e ( ) , t r i p l e ( ) , s q u a r e ( ) , and c u b e ( ) , each of which returns a
value. You could write
A n s w e r = ( d o u b l e ( t r i p l e ( s q u a r e ( c u b e ( m y V a l u e ) ) ) ) ) ;
- 11-
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:
u n s i g n e d l o n g m y V a l u e = 2 ;
u n s i g n e d l o n g c u b e d = c u b e ( m y V a l u e ) ; / / c u b e d = 8
u n s i g n e d l o n g s q u a r e d = s q u a r e ( c u b e d ) ; / / s q u a r e d = 6 4
u n s i g n e d l o n g t r i p l e d = t r i p l e ( s q u a r e d ) ; / / t r i p l e d = 1 9 6
u n s i g n e d l o n g A n s w e r = d o u b l e ( t r i p l e d ) ; / / A n s w e r = 3 9 2
Now each intermediate result can be examined, and the order of execution is explicit.
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.
- 12-
O u t p u t : M a i n . B e f o r e s w a p , x : 5 y : 1 0
S w a p . B e f o r e s w a p , x : 5 y : 1 0
S w a p . A f t e r s w a p , x : 1 0 y : 5
M a i n . A f t e r s w a p , x : 5 y : 1 0
Analysis: This program initializes two variables in m a i n ( ) and then passes them to the
s w a p ( ) function, which appears to swap them. When they are examined again in m a i n ( ) ,
however, they are unchanged! The variables are initialized on line 9, and their values are
displayed on line 11. s w a p ( ) is called, and the variables are passed in. Execution of the
program switches to the s w a p ( ) function, where on line 21 the values are printed again.
They are in the same order as they were in m a i n ( ) , 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 s w a p ( ) function, the values are swapped. Execution then returns to line 13, back in
m a i n ( ) , where the values are no longer swapped.
As you've figured out, the values passed in to the s w a p ( ) function are passed by value,
meaning that copies of the values are made that are local to s w a p ( ) . These local variables
are swapped in lines 23 to 25, but the variables back in m a i n ( ) are unaffected.
In C++, passing by reference is accomplished in two ways: using pointers and using
references. The syntax is different, but the net effect is the same. Rather than a copy being
created within the scope of the function, the actual original object is passed into the
function. Passing an object by reference allows the function to change the object being
referred to. In previous section showed that a call to the s w a p ( ) function did not affect the
values in the calling function.
- 13-
2 0 : i n t t e m p ;
2 1 :
2 2 : c o u t < < " S w a p . B e f o r e s w a p , r x : " < < r x < < " r y : " < < r y < < " \ n " ;
2 3 :
2 4 : t e m p = r x ;
2 5 : r x = r y ;
2 6 : r y = t e m p ;
2 7 :
2 8 : c o u t < < " S w a p . A f t e r s w a p , r x : " < < r x < < " r y : " < < r y < < " \ n " ;
2 9 :
3 0 : }
O u t p u t : M a i n . B e f o r e s w a p , x : 5 y : 1 0
S w a p . B e f o r e s w a p , r x : 5 r y : 1 0
S w a p . A f t e r s w a p , r x : 1 0 r y : 5
M a i n . A f t e r s w a p , x : 1 0 , y : 5
A n a y l s i s : wo variables are declared on line 10 and their values are printed on line 12.
T
On line 13, the function s w a p ( ) is called, but note that x and y , not their addresses, are
passed. The calling function simply passes the variables.
When s w a p ( ) is called, program execution jumps to line 18, where the variables are
identified as references. Their values are printed on line 22, but note that no special
operators are required. These are aliases for the original values, and can be used as such.
On lines 24-26, the values are swapped, and then they're printed on line 28. Program
execution jumps back to the calling function, and on line 14, the values are printed in
m a i n ( ) . Because the parameters to s w a p ( ) are declared to be references, the values from
m a i n ( ) are passed by reference, and thus are changed in m a i n ( ) as well. References
provide the convenience and ease of use of normal variables.
Functions return a value or return v o i d . V o i d is a signal to the compiler that no value will
be returned. To return a value from a function, write the keyword r e t u r n followed by the
value you want to return. The value might itself be an expression that returns a value. For
example:
r e t u r n 5 ;
r e t u r n ( x > 5 ) ;
r e t u r n ( M y F u n c t i o n ( ) ) ;
These are all legal r e t u r n statements, assuming that the function M y F u n c t i o n ( ) itself
returns a value. The value in the second statement, r e t u r n ( 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 (f a l s e ) or 1
(t r u e ), not the value of x .
- 14-
Listing 5.6. A demonstration of multiple return statements.
1 : / / L i s t i n g 5 . 6 - d e m o n s t r a t e s m u l t i p l e r e t u r n
2 : / / s t a t e m e n t s
3 :
4 : # i n c l u d e < i o s t r e a m . h >
5 :
6 : i n t D o u b l e r ( i n t A m o u n t T o D o u b l e ) ;
7 :
8 : i n t m a i n ( )
9 : {
1 0 :
1 1 : i n t r e s u l t = 0 ;
1 2 : i n t i n p u t ;
1 3 :
1 4 : c o u t < < " E n t e r a n u m b e r b e t w e e n 0 a n d 1 0 , 0 0 0 t o d o u b l e : " ;
1 5 : c i n > > i n p u t ;
1 6 :
1 7 : c o u t < < " \ n B e f o r e d o u b l e r i s c a l l e d . . . " ;
1 8 : c o u t < < " \ n i n p u t : " < < i n p u t < < " d o u b l e d : " < < r e s u l t < < " \ n " ;
1 9 :
2 0 : r e s u l t = D o u b l e r ( i n p u t ) ;
2 1 :
2 2 : c o u t < < " \ n B a c k f r o m D o u b l e r . . . \ n " ;
2 3 : c o u t < < " \ n i n p u t : " < < i n p u t < < " d o u b l e d : " < < r e s u l t < < " \ n " ;
2 4 :
2 5 :
2 6 : r e t u r n 0 ;
2 7 : }
2 8 :
2 9 : i n t D o u b l e r ( i n t o r i g i n a l )
3 0 : {
3 1 : i f ( o r i g i n a l < = 1 0 0 0 0 )
3 2 : r e t u r n o r i g i n a l * 2 ;
3 3 : e l s e
3 4 : r e t u r n - 1 ;
3 5 : c o u t < < " Y o u c a n ' t g e t h e r e ! \ n " ;
3 6 : }
O u t p u t : E n t e r a n u m b e r b e t w e e n 0 a n d 1 0 , 0 0 0 t o d o u b l e : 9 0 0 0
B e f o r e d o u b l e r i s c a l l e d . . .
i n p u t : 9 0 0 0 d o u b l e d : 0
B a c k f r o m d o u b l e r . . .
i n p u t : 9 0 0 0 d o u b l e d : 1 8 0 0 0
E n t e r a n u m b e r b e t w e e n 0 a n d 1 0 , 0 0 0 t o d o u b l e : 1 1 0 0 0
B e f o r e d o u b l e r i s c a l l e d . . .
i n p u t : 1 1 0 0 0 d o u b l e d : 0
B a c k f r o m d o u b l e r . . .
i n p u t : 1 1 0 0 0 d o u b l e d : - 1
- 15-
Analysis: A number is requested on lines 14 and 15, and printed on line 18, along with the
local variable result. The function D o u b l e r ( ) is called on line 20, and the input value is
passed as a parameter. The result will be assigned to the local variable r e s u l t , and the
values will be reprinted on lines 22 and 23. On line 31, in the function D o u b l e r ( ) , 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!
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
l o n g m y F u n c t i o n ( i n t ) ;
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
l o n g m y F u n c t i o n ( i n t x = 5 0 ) ;
l o n g m y F u n c t i o n ( i n t = 5 0 ) ;
The function definition is not changed by declaring a default parameter. The function
definition header for this function would be
l o n g m y F u n c t i o n ( i n t x )
If the calling function did not include a parameter, the compiler would fill x with the
default value of 5 0 . 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.
l o n g m y F u n c t i o n ( i n t P a r a m 1 , i n t P a r a m 2 , i n t P a r a m 3 ) ;
- 16-
you can assign a default value to P a r a m 2 only if you have assigned a default value to
P a r a m 3 . You can assign a default value to P a r a m 1 only if you've assigned default values to
both P a r a m 2 and P a r a m 3 . Listing 5.7 demonstrates the use of default values.
O u t p u t : F i r s t a r e a e q u a l s : 1 0 0 0 0
S e c o n d t i m e a r e a e q u a l s : 5 0 0 0
T h i r d t i m e a r e a e q u a l s : 2 5 0 0
- 17-
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.
When you define a function, normally the compiler creates just one set of instructions in
memory. When you call the function, execution of the program jumps to those instructions,
and when the function returns, execution jumps back to the next line in the calling function.
If you call the function 10 times, your program jumps to the same set of instructions each
time. This means there is only one copy of the function, not 10.
There is some performance overhead in jumping in and out of functions. It turns out that
some functions are very small, just a line or two of code, and some efficiency can be gained
if the program can avoid making these jumps just to execute one or two instructions. When
programmers speak of efficiency, they usually mean speed: the program runs faster if the
function call can be avoided.
If a function is declared with the keyword i n l i n e , the compiler does not create a real
function: it copies the code from the inline function directly into the calling function. No
jump is made; it is just as if you had written the statements of the function right into the
calling function.
Syntax
Note that inline functions can bring a heavy cost. If the function is called 10 times, the
inline code is copied into the calling functions each of those 10 times. The tiny
improvement in speed you might achieve is more than swamped by the increase in size of
the executable program. What's the rule of thumb? If you have a small function, one or two
statements, it is a candidate for i n l i n e . When in doubt, though, leave it out. Listing 5.9
demonstrates an i n l i n e function.
- 18-
3 : # i n c l u d e < i o s t r e a m . h >
4 :
5 : i n l i n e i n t D o u b l e ( i n t ) ;
6 :
7 : i n t m a i n ( )
8 : {
9 : i n t t a r g e t ;
1 0 :
1 1 : c o u t < <
" E n t e r a n u m b e r t o w o r k w i t h : " ;
1 2 : c i n > > t a r g e t ;
1 3 : c o u t < < " \ n " ;
1 4 :
1 5 : t a r g e t = D o u b l e ( t a r g e t ) ;
1 6 : c o u t < < " T a r g e t : " < < t a r g e t < < e n d l ;
1 7 :
1 8 : t a r g e t = D o u b l e ( t a r g e t ) ;
1 9 : c o u t < < " T a r g e t : " < < t a r g e t < < e n d l ;
2 0 :
2 1 :
2 2 : t a r g e t = D o u b l e ( t a r g e t ) ;
2 3 : c o u t < < " T a r g e t : " < < t a r g e t < < e n d l ;
2 4 : r e t u r n 0 ;
2 5 : }
2 6 :
2 7 : i n t D o u b l e ( i n t t a r g e t )
2 8 : {
2 9 : r e t u r n 2 * t a r g e t ;
3 0 : }
O u t p u t : E n t e r a n u m b e r t o w o r k w i t h : 2 0
T a r g e t : 4 0
T a r g e t : 8 0
T a r g e t : 1 6 0
t a r g e t = D o u b l e ( t a r g e t ) ;
By the time your program executes, the instructions are already in place, compiled into the
OBJ file. This saves a jump in the execution of the code, at the cost of a larger program.
- 19-
C = 5 (F-32) / 9
Given this formula, it is trivial to write a C++ function; with declarations and braces
removed, the one line formula above translates into one line C++ statement . Mathematical
functions are sometimes defined in a less standard form. For example we can define a
function f, valid on non negative integers, that satisfies:
f(0) = 0
f(x) = 2, f(x-1) +x2 for x>0, x e Z
From this definition,
f(3) = 2f(2) + 32
= 2f(2) + 9 now we should compute f(2) first.
f(2) = 2f(1) + 22
= 2f(1) + 4 now we should compute f(1) first.
f(1) = 2f(0) + 12
= 2f(0) + 1 now we should compute f(0) first.
f(0) = 0
ð f(1) = 2 (0) + 1
= 0 +1 = 1
ð f(2) = 2 (1) + 4
= 2 +4 = 6
=> f(3) = 2 (6) + 9
= 12 + 9 = 21
A function that is defined in terms of it self is called recursive. f(0) in the above function is
the value which is the function directly known with out resorting to recursion. Such case is
known as the base case. A recursive function without a base case is meaningless. When
writing recursive routines, it is crucial to keep in mind the four basic rules of recursion.
These are:
1. Base case: you must always have some Base case, which can be solved with out
recursion.
2. Making progress: for the cases that are to be solved recursively, the recursive call
must always be to a case that makes progress to ward a base case.
3. Design rule: Assume that all recursive calls work without fail. In order to find f(n)
recursively you can assume f(n-k) will work (k ³ 1)
- 20-
4. Compound interest rule: never duplicate work by solving the same instance of a
problem in separate recursive rule.
Some examples of recursive problems
a) The factorial function f(n) = n!
f(n) can be defined as:
f(N) = N x (N-1) x (N-2) x (N-3) x ..x 1 this is iterative defn
- 21-
if (n == 1|| n==2) return 1;
else if n > 2
{
int val1 = i_th_fibonacci_element ( n-1);
int val2 = i_th_fibonacci_element ( n-2);
return (val1 +val2);
}
}
Some function cannot be implemented easily without recursion. Other functions,
which do admit both iterative and recursive solution are easier to understand in their
recursive form. So recursion is an essential tool for computer scientist.
However recursion is very expensive in terms of time complexity of a program so
that it is not generally advisable to use recursion to problems that is simple to
implement in iterative way. This is because of the time requirement to manipulate
stack at each function call.
Some more recursive problems and their implementation
C) The Euclidean algorithm
A positive integer d is a divisor of a larger integer n if n is some multiple of d; i.e. n
= k. d for some positive integer k
A positive integer is a common divisor of two larger integer n and m if it is a divisor
of both m & n. d is said to be greatest common divisor of two large integers m& n,
if it is a common divisor of m & n, and no other common divisor of m & n is greater
than d.
This can be denoted as d = gcd (m, n).
It might seem that finding gcd of large integers could be very tedious and time
consuming. But fortunately the ancient Greek scientist Euclid discovered a clever
recursive algorithm as follow.
- 22-
}
D) Binary Search
Binary search for an element x in sorted array A[] looks at the middle of the array. If that is
not x (the middle element of A x) then it continues the search in the half that potential
could hold x. Assuming the array is sorted in ascending order, the potential half array is the
left array if the middle is larger than x other wise the right the potential list to hold x. The
process contributes this recursive step until either x is found or the sub array is empty.
What should be returned in the index of the array that hold x if it founds or -1.
Implementation
int find (float*a, int start, int stop, float x )
{
if (start > stop) return -1;
int mid = (start + stop) / 2;
if (x == a[mid] return mid;
if (x <a[mid] return find (a, start, mid -1, x );
else return find (a, mid +1, start , x );
}
The call from other program to this function may look like
index = find(list, 0, size-1);
e) The tower of Hanoi
A classic example to demonstrate the power of recursion in problem solving is the tower of
Hanoi puzzle. In this puzzle we are given three pegs labeled A, B and C. Initially a tower of
N disk of different sizes is stacked on peg A in order of decreasing size. That is, the largest
disk is on the bottom, and the smallest disk is on the top of the tower as shown bellow
A B C
The objective is to transfer the entire disks from peg A to one of the other pegs, say C
moving only one disk at one time, never moving the larger disk on the top of smaller disk.
The solution follows a simple procedure.
if N = 1 then just move it to destination peg. Otherwise
- 23-
1- Reduce the problem that say move the top N-1 disk from peg A to B using C .
2- Move the Nth disk (which is the largest) from peg A to C.
3- Now move the N-1 disks from B to C using A. And this will tell us what to do for
any N.
The implementation is as follows:
#include <iostream.h>
#include <conio.h>
void Form(int N, char pegA ,char pegB , char pegC )
{
if (N==1)
cout<<"move top disk on peg "<<pegA<<" to peg "<<pegC<<endl;
else
{
Form(N-1,pegA,pegC,pegB);
cout<<"move top disk on peg "<<pegA<<" to peg "<<pegC<<endl;
Form(N-1,pegB,pegA,pegC);
}
}
int main ()
{
char F;
int N;
cout<<"How many disks ?=====>";cin>>N;
Form(N,'A','B','C') ;
getch () ;
return 0;
}
- 24-