


                                                              Chapter 4
                                                              FUNCTIONS

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

      Prototyping allows the compiler to do additional type checking for
      your function calls which can aid in detecting 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 INLINE CODE
      on page 4-4 of this chapter.


      PROTOTYPES
      _________________________________________________________________

      Examine the file named PROTYPE1.CPP for our      ================
      first look at a prototype and an illustration of   PROTYPE1.CPP
      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 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.  You will get an apparently good compile and
      link, but the program may do some very strange things when it is
      executed if the correct number of parameters are not used.

      To write the prototype, simply copy the header from the function
      to the beginning of the program and append a semicolon to the end
      as a signal to the compiler that this is not a function but a
      prototype.  The variable names given in the prototype are optional

                                                               Page 4-1
      
                                                  Chapter 4 - Functions

      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
      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, 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 calling 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 do
      certain wrong things.  Change the actual parameters in line 12 to

                                                               Page 4-2
      
                                                  Chapter 4 - Functions

      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.  Finally, you will find
      that there is a returned value that is not used in the calling
      program.  You should have a good feeling that prototyping is doing
      something good for you after making all of those 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 good 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 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 this 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 for the

                                                               Page 4-3
      
                                                  Chapter 4 - Functions

      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.



      PASS BY REFERENCE
      _________________________________________________________________

      Examine The file 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.  This
      example program illustrates 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 this is a little neater.

      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 variable since the actual variable from the main
      program will be used in the function.  In the function itself, in
      lines 21 through 24, the variable in2 is used just like any other
      variable but 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.

      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
      treatedlike a variable with the reserved word VAR used in front of
      it usually referred to as a call by reference.

      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.




                                                               Page 4-4
      
                                                  Chapter 4 - Functions

      DEFAULT PARAMETERS
      _________________________________________________________________

      Examine the file named DEFAULT.CPP for an         ===============
      example of the use of default parameters in C++.    DEFAULT.CPP
      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 (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 (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.


      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

                                                               Page 4-5
      
                                                  Chapter 4 - Functions

      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++, verified that this
      is operating correctly.)

      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 correct.

      Be sure to compile and execute DEFAULT.CPP after you understand it.
      Note that the funny output 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 of     VARARGS.CPP
      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.  Of course these are available for use with C++ also,
      but we need a way to eliminate the strong 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
      of type int is required as the first parameter, then no further
      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

                                                               Page 4-6
      
                                                  Chapter 4 - Functions

      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 22 will be called and executed.  If the
      single actual parameter is of type float, the function beginning
      in line 27 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 can also be used to
      determine which function will be called, but that is of no help in
      this case because the stream output functions called in lines 15
      through 19 are typeless as mentioned earlier.

      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 compilable with newer compilers.
      It may be best to include the keyword as an indication to future
      readers of your code that function name overloading is
      intentionally used here.

      The actual selection 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 time 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.


                                                               Page 4-7
      
                                                  Chapter 4 - Functions


      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 value 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 by the present
           program.























                                                               Page 4-8
      