


                                                        Chapter 4
                                                        FUNCTIONS

This chapter discusses enhancements in the capabilities of 
functions that have been made to C++.  These changes make 
programming more convenient and permit the compiler to do further 
checking for errors.  A fair amount of time is also spent in this 
chapter teaching the modern form of function definition and 
prototyping.

Prototyping allows the compiler to do additional type checking 
for your function calls which can detect some programming errors.  
The first two example programs in this chapter are designed to 
teach prototyping and what it will do for you.  Prototyping is a 
relatively new addition to C, so even some experienced C 
programmers are not familiar with it.  If you have experience 
with prototyping you can skip directly to the section named PASS 
BY REFERENCE on page 4-4 of this chapter.


PROTOTYPES
-----------------------------------------------------------------
Examine the file named PROTYPE1.CPP for our    ==================
first look at a prototype and an illustration     PROTYPE1.CPP
of how it is used.  The prototyping used in    ==================
C++ is no different than that used in ANSI-C.  
Actually, many C programmers take a rather dim view of 
prototyping and seem reluctant to use it, but with C++ it is 
considerably more important and is in much heavier use.  In fact, 
prototyping is required to be used in some situations in C++.

A prototype is a limited model of a more complete entity to come 
later.  In this case, the full function is the complete entity to 
come later and the prototype is illustrated in line 4.  The 
prototype gives a model of the interface to the function that can 
be used to check the calls to the function for the proper number 
of parameters and the correct types of parameters.  Each call to 
the function named do_stuff() must have exactly three parameters 
or the compiler will give an error message.  In addition to the 
correct number of parameters, the types must be compatible or the 
compiler will issue an error message.  Notice that when the 
compiler is working on lines 12 and 13, the type checking can be 
done based on the prototype in line 4 even though the function 
itself is not yet defined.  If the prototype is not given, the 
number of parameters will not be checked, nor will the types of 
the parameters be checked.  Without a prototype, if you have the 
wrong number of parameters, you will get an apparently good 
compile and link, but the program may do some very strange things 
when it is executed.

To write the prototype, simply copy the header from the function 
to the beginning of the program and append a semicolon to the end 

                                                         Page 4-1

                                            Chapter 4 - Functions

as a signal to the compiler that this is not a function but a 
prototype.  The variable names given in the prototype are 
optional and act merely as comments to the program reader since 
they are completely ignored by the compiler.  You could replace 
the variable name wings in line 4 with your first name and there 
would be no difference in compilation.  Of course, the next 
person that had to read your program would be somewhat baffled 
with your choice of variable names.

In this case, the two function calls to this function, given in 
lines 12 and 13, are correct so no error will be listed during 
compilation.

Even though we wish to use the char type for eyes in the 
function, we wish to use it as a number rather than as a 
character.  The cast to int in line 20 is required to force the 
printout of the numerical value rather than an ASCII character.  
The next example program is similar but without the cast to int.


COMPATIBLE TYPES
-----------------------------------------------------------------
We mentioned compatible types earlier so we should review them 
just a bit in order to make our discussion of prototyping 
complete.  Compatible types are any simple types that can be 
converted from one to another in a meaningful way.  For example, 
if you used an integer as the actual parameter and the function 
was expecting a float type as the formal parameter, the system 
would do the conversion automatically, without mentioning it to 
you.  This is also true of a float changing to a char, or a char 
changing to an int.  There are definite conversion rules which 
would be followed.  These rules are given in great detail in 
section 3.2 of the draft of the ANSI-C standard and are also 
given on page 198 of the second edition of the K&R reference.

If we supplied a pointer to an integer as the actual parameter 
and expected an integer as the formal parameter in the function, 
the conversion would not be made because they are two entirely 
different kinds of values.  Likewise, a structure would not be 
converted automatically to a long float, an array, or even to a 
different kind of structure, because they are all incompatible 
and cannot be converted in any meaningful manner.  The entire 
issue of type compatibility as discussed in chapter 2 of this 
tutorial applies equally well to the compatibility of types when c
alling a function.  Likewise, the type specified as the return 
type, in this case void, must be compatible with the expected 
return type in the calling statement, or the compiler will issue 
a warning.

HOW DOES PROTOTYPING WORK?
-----------------------------------------------------------------
This is your chance to try prototyping for yourself and see how 
well it works and what kinds of error messages you get when you 

                                                         Page 4-2

                                            Chapter 4 - Functions

do certain wrong things.  Change the actual parameters in line 12 
to read (12.2, 13, 12345) and see what the compiler says about 
that change.  It will probably say nothing because they are all 
type compatible.  If you change it to read (12.0, 13), it will 
issue a warning or error because there are not enough arguments 
given.  Likewise you should receive an error message if you 
change one of the parameters in line 13 to an address by putting 
an ampersand in front of one of the variable names.  Finally, 
change the first word in line 4 from void to int and see what 
kind of error message is given.  You will first be required to 
make the function header in line 16 agree with the prototype, 
then you will find that there is not a variable returned from 
the function.  You should have a good feeling that prototyping 
is doing something worthwhile for you after making these changes.

Be sure to compile and execute this program then make the changes 
recommended above, attempting to compile it after each change.


A LITTLE MORE PROTOTYPING
-----------------------------------------------------------------
Examine the next example program named         ==================
PROTYPE2.CPP for a little more information on     PROTYPE2.CPP
prototyping.  This program is identical to     ==================
the last one except for a few small changes.  
The variable names have been omitted from the prototype in line 4 
merely as an illustration that they are interpreted as comments 
by the C++ compiler.  The function header is formatted 
differently to allow for a comment alongside each of the actual 
parameters.  This should make the function header a little more 
self explanatory.  However, you should remember that comments 
should not be used to replace careful selection of variable 
names.  In this particular case, the comments add essentially 
nothing to the clarity of the program.


WHAT DOES PROTOTYPING COST?
-----------------------------------------------------------------
Prototyping is essentially free because it costs absolutely 
nothing concerning the run time size or speed of execution.  
Prototyping is a compile time check only, and slows down the 
compile time a negligible amount because of the extra checking 
that the compiler must do.  If prototyping finds one error for 
you that you would have had to find with a debugger, it has more 
than paid for itself for use in an entire project.  I once spent 
12 hours of debugging time to find that I forgot to pass the 
address of a variable to a function.  Prototyping would have 
found the error on the first compilation of that 2000 line 
program.

The only price you pay to use prototyping is the extra size of 
the source files because of the prototypes, and the extra time 


                                                         Page 4-3

                                            Chapter 4 - Functions

for the compiler to read the prototypes during the compilation 
process, but both costs are negligible.

Be sure to compile and execute this example program.  You will 
find that it is identical to the last example program, except 
for the changes in the prototype and the removal of the cast 
from the last line of the function.


PASS BY REFERENCE
-----------------------------------------------------------------
Examine the program named PASSREF.CPP for an    =================
example of a pass by reference, a construct        PASSREF.CPP
which is not available in ANSI-C.  The          =================
reference variable was mentioned in chapter 1 
and it was recommended there that you don't use it in the manner 
illustrated there.  This example program illustrates a situation 
where it can be used to your advantage.  The pass by reference 
allows the passing of a variable to a function and returning the 
changes made in the function to the main program.  In ANSI-C the 
same effect can be seen when a pointer to a variable is passed 
to a function, but use of a reference variable is a little 
cleaner.

Observe the prototype in line 4 where the second variable has an 
ampersand in front of the variable name.  The ampersand instructs 
the compiler to treat this variable just like it were passed a 
pointer to the actual variable passed from the calling function.  
It acts like the actual variable from the main program is used in 
the function.  In the function itself, in lines 22 through 25, 
the variable in2 is used just like any other variable but it acts 
like we are using the variable passed to this function from the 
main program not a copy of it.  The other variable named in1 is 
treated just like any other normal variable in ANSI-C.  In 
effect, the name in2 is a synonym for the variable named index in 
the main program, but the name in1 refers to a copy of the 
variable count from the main program.  In actual practice, a 
pointer is passed to the function and it is automatically 
dereferenced when used in the function.  This is transparent to 
you, the programmer.

If you prefer to omit the variable names in the prototypes, you 
would write the prototype as follows;

   void fiddle(int, int&);

If you are a Pascal programmer, you will recognize that the 
variable named in1 is treated just like a normal parameter in a 
Pascal call, a call by value.  The variable named in2 however, is 
treated like a variable with the reserved word VAR used in front 
of it, usually referred to as a call by reference.  As mentioned 
earlier, the reference variable used in C++ is actually a self 


                                                         Page 4-4

                                            Chapter 4 - Functions

dereferencing pointer which refers to, or points to, the original 
value.

When you compile and execute this program, you will find that the 
first variable got changed in the function but was returned to 
its original value when we returned to the main program.  The 
second variable however, was changed in the function and the new 
value was reflected back into the variable in the main program 
which we can see when the values are listed on the monitor.


DEFAULT PARAMETERS
-----------------------------------------------------------------
Examine the file named DEFAULT.CPP for an       =================
example of the use of default parameters in        DEFAULT.CPP
C++.  This program really looks strange since   =================
it contains default values for some of the 
parameters in the prototype, but these default values are very 
useful as we will see shortly.

This prototype says that the first parameter named length must be 
given for each call of this function because a default value is 
not supplied.  The second parameter named width, however, is not 
required to be specified for each call, and if it is not 
specified, the value 2 will be used for the variable width within 
the function.  Likewise, the third parameter is optional, and if 
it is not specified, the value of 3 will be used for height 
within the function.

In line 11 of this program, all three parameters are specified so 
there is nothing unusual about this call from any other function 
call we have made.  Only two values are specified in line 12 
however, so we will use the default value for the third parameter 
and the system acts as if we called it with get_value(x, y, 3) 
since the default value for the third value is 3.  In line 13, we 
only specified one parameter which will be used for the first 
formal parameter, and the other two will be defaulted.  The 
system will act as if we had called the function with 
get_volume(x, 2, 3).  Note that the output from these three lines 
is reversed.  This will be explained shortly.

There are a few rules which should be obvious but will be stated 
anyway.  Once a parameter is given a default value in the list of 
formal parameters, all of the remaining must have default values 
also.  It is not possible to leave a hole in the middle of the 
list, only the trailing values can be defaulted.  Of course, the 
defaulted values must be of the correct types or a compiler error 
will be issued.  The default values can be given in either the 
prototype or the function header, but not in both.  If they are 
given in both places, the compiler must not only use the default 
value, but it must carefully check to see that both values are 
identical.  This could further complicate an already very 
complicated problem, that of writing a C++ compiler.

                                                         Page 4-5

                                            Chapter 4 - Functions
                                   
As a matter of style, it is highly recommended that the default 
values be given in the prototype rather than in the function.  
The reason will be obvious when we begin using object oriented 
programming techniques.


WHY IS THE OUTPUT SCRAMBLED?
-----------------------------------------------------------------
When the compiler finds a cout statement, the complete line of 
code is initially scanned from right to left to evaluate any 
functions, then the data is output field by field from left to 
right.  Therefore in line 11, get_value() is evaluated with its 
internal output displayed first.  Then the fields of the cout are 
displayed from left to right with "Some box data is" displayed 
next.  Finally, the result of the return from get_value() is 
output in int format, the type of the returned value.  The end 
result is that the output is not in the expected order when lines 
11 through 13 are executed.  (The output is not what you would 
intuitively expect to happen so appears to be a deficiency in the 
language.  A call to Borland International, the writers of Turbo 
C++ and Borland C++, verified that this is operating correctly.)

We still have the problem of mixing cout and printf() output as 
discussed in chapter 1 while studying the program named 
MESSAGE.CPP.  Eventually, all conforming compilers will overcome 
this problem.

Lines 15 through 18 are similar to any two of the lines of code 
in lines 11 through 13, but are each separated into two lines so 
the output is in the expected order. 

Be sure to compile and execute DEFAULT.CPP after you understand 
it.  Note that the funny output order will appear again later in 
this tutorial.


VARIABLE NUMBER OF ARGUMENTS
-----------------------------------------------------------------
Examine the program named VARARGS.CPP for an    =================
illustration of the use of a variable number       VARARGS.CPP
of arguments in a function call. We have gone   =================
to a lot of trouble to get the compiler to 
help us by carefully checking how many parameters we use in the 
function calls and checking the types of the parameters.  On rare 
occasion, we may wish to write a function that uses a variable 
number of parameters.  The printf() function is a good example of 
this.  ANSI-C has a series of three macros available in the 
"stdarg.h" header file to allow the use of a variable number of 
arguments.  These are available for use with C++ also, but we 
need a way to eliminate the stronger type checking that is done 
with all C++ functions.  The three dots illustrated in line 6 
will do this for us.  This prototype says that a single argument 


                                                         Page 4-6

                                            Chapter 4 - Functions

of type int is required as the first parameter, then no further 
type checking will be done by the compiler.

You will note that the main program consists of three calls to 
the function, each with a different number of parameters, and the 
system does not balk at the differences in the function calls.  
In fact, you could put as many different types as you desire in 
the calls.  As long as the first one is an int type variable, the 
system will do its best to compile and run it for you.  Of course 
the compiler is ignoring all type checking beyond the first 
parameter so it is up to you to make sure you use the correct 
parameter types in this call.

In this case, the first parameter gives the system the number of 
additional parameters to look for and handle.  In this simple 
program, we simply display the numbers on the monitor to 
illustrate that they really did get handled properly.

Of course, you realize that using a variable number of arguments 
in a function call can lead to very obscure code and should be 
used very little in a production program, but the capability 
exists if you need it.  Be sure to compile and execute this 
program.


FUNCTION NAME OVERLOADING
-----------------------------------------------------------------
Examine the file named OVERLOAD.CPP for an     ==================
example of a program with the function names      OVERLOAD.CPP
overloaded.  This is not possible in ANSI-C,   ==================
but is perfectly legal and in fact used quite 
regularly in C++.  At first this will seem a bit strange, but it 
is one of the keystones of object oriented programming.  You will 
see its utility and purpose very clearly in later chapters of 
this tutorial.

You will notice in this example program that there are three 
functions, in addition to the main function, and all three have 
the same name.  Your first question is likely to be, "Which 
function do you call when you call do_stuff()?"  That is a valid 
question and the answer is, the function that has the correct 
number of formal parameters of the correct types.  If do_stuff() 
is called with an integer value or variable as its actual 
parameter, the function beginning in line 23 will be called and 
executed.  If the single actual parameter is of type float, the 
function beginning in line 28 will be called, and if two floats 
are specified, the function beginning in line 33 will be called.

It should be noted that the return type is not used to determine 
which function will be called.  Only the types of the formal 
parameters are used to determine which overloaded function will 
be called.


                                                         Page 4-7

                                            Chapter 4 - Functions

The keyword overload used in line 4 tells the system that you 
really do intend to overload the name do_stuff, and the 
overloading is not merely an oversight.  This is only required in 
C++ version 1.2.  C++ version 2.0 and greater do not require the 
keyword overload but allows it to be used optionally in order to 
allow the existing body of C++ code to be compatible with newer 
compilers.  It is not necessary to use this keyword because, when 
overloading is used in C++, it is generally used in a context in 
which it is obvious that the function name is overloaded.

When the final C++ standard is completed, the use of this word 
may not be permitted.  As was mentioned earlier, the C++ language 
is changing and we must be willing to change with it.

The actual selection of which function to call is done at compile 
time, not at execution time, so the program is not slowed down.  
If each of the overloaded function names were changed to 
different names, each being unique, there would be no difference 
in execution size or speed of the resulting program.

Overloading of function names may seem very strange to you, and 
it is strange if you are used to the rules of K&R or ANSI-C 
programming.  As you gain experience with C++, you will feel 
very comfortable with this, and you will use it a lot in your C++ 
programming.

Note the use of the keyword const used in some of the function 
prototypes and headers.  Once again, this prevents the programmer 
from accidentally changing the formal parameter within the 
function.  In a function as short as these, there is no real 
problem with an accidental assignment.  In a real function that 
you occasionally modify, you could easily forget the original 
intention of the use of a variable and attempt to change it 
during an extended debugging session.

PROGRAMMING EXERCISES
-----------------------------------------------------------------
1.  Change the type of wings in the prototype of PROTYPE1.CPP to 
    float so that it disagrees with the function definition to 
    see if you get a compilation error.
    
2.  Change the function definition in PROTYPE1.CPP to agree with 
    the changed prototype.  Compile and execute the program 
    without changing the calls in lines 12 and 13.  Explain the 
    results.

3.  In DEFAULT.CPP, remove the default value from the prototype 
    for height only to see what kind of compiler error you get.  
    Only the last values of the list can be defaulted. 

4.  In OVERLOAD.CPP, change the names of the three functions so 
    that each is a unique name and compare the size of the 
    resulting executable file with that given for the present 
    program.
                                                         Page 4-8
