Chapter 3
FUNCTIONS

3.1 & 3.2 Introduction

A C++ function is a named module (a block of program statements) for performing a given task.
Class is an Object-Oriented (OO) way for writing reusable modular programs in C++.

3.3 Math Library Functions

#include <math.h>
#include <iostream.h>
int main()
{ float f;
    f = sqrt(16.0);            // calling/invoking a function,
    cout << f;
    cout << sqrt(64);
    return 0;
}

Fig 3.2: ceil, cos, exp, ...
/* Testing the math library functions */
#include <iostream.h>
#include <iomanip.h>
#include <math.h>

int main()
{
   cout << setiosflags( ios::fixed | ios::showpoint )
        << setprecision( 1 )
        << "sqrt(" << 900.0 << ") = " << sqrt( 900.0 )
        << "\n sqrt(" << 9.0 << ") = " << sqrt( 9.0 )
        << "\n exp(" << 1.0 << ") = " << setprecision( 6 )
        << exp( 1.0 ) << "\nexp(" << setprecision( 1 ) << 2.0
        << ") = " << setprecision( 6 ) << exp( 2.0 )
        << "\n log(" << 2.718282 << ") = " << setprecision( 1 )
        << log( 2.718282 ) << "\n log(" << setprecision( 6 )
        << 7.389056 << ") = " << setprecision( 1 )
        << log( 7.389056 ) << endl;
   cout << "log10(" << 1.0 << ") = " << log10( 1.0 )
        << "\n log10(" << 10.0 << ") = " << log10( 10.0 )
        << "\n log10(" << 100.0 << ") = " << log10( 100.0 )
        << "\n fabs(" << 13.5 << ") = " << fabs( 13.5 )
        << "\n fabs(" << 0.0 << ") = " << fabs( 0.0 )
        << "\n fabs(" << -13.5 << ") = " << fabs( -13.5 ) << endl;
   cout << "ceil(" << 9.2 << ") = " << ceil( 9.2 )
        << "\n ceil(" << -9.8 << ") = " << ceil( -9.8 )
        << "\n floor(" << 9.2 << ") = " << floor( 9.2 )
        << "\n floor(" << -9.8 << ") = " << floor( -9.8 ) << endl;
   cout << "pow(" << 2.0 << ", " << 7.0 << ") = "
        << pow( 2.0, 7.0 ) << "\npow(" << 9.0 << ", "
        << 0.5 << ") = " << pow( 9.0, 0.5 )
        << setprecision(3) << "\nfmod("
        << 13.675 << ", " << 2.333 << ") = "
  << fmod( 13.675, 2.333 ) << setprecision( 1 )
        << "\n sin(" << 0.0 << ") = " << sin( 0.0 )
        << "\n cos(" << 0.0 << ") = " << cos( 0.0 )
        << "\n tan(" << 0.0 << ") = " << tan( 0.0 ) << endl;
   return 0;
}
 

3.4 Functions

Software Reusability (Code Reuse)

    f = sqrt(16.0);

Five points about using a function:

3.5 & 3.6 Function Definitions and Prototypes
#include <iostream.h>

int cube( int y );    // A function prototype: tells the compiler about the interface of the function.
                            // It's prototye must be given before a function can be used.
                            // Prototypes of library functions are given in the head files (.h files)

int main()
{
   int x, z;
   z = cube(10);  // function invocation, argument, return value
   cout << "The cubic of 10 is " << z;
 
   cout << "Please enter an integer : " << endl;
  cin >> x;
  z = cube(x);
  cout << "The cubic of " << x << "is " << z << endl;

  x = 20;
  cout << "The cubic of " << x << "is " << cube( x ) << endl;

   return 0;
}

// function definition
int cube( int y )                         // function header
{                                                         // start of the function body
    int z;
     z = y * y * y;
     return z;
}                                                          // end of the function body
 

    return-value-type function-name(parameter-list)
    {
        declarations
        statements
        return value
    }
 
     int cube( int y )                                    // function header
    {                                                         // start of the function body
     return y * y * y;
    }                                                          // end of the function body
 
// Finding the maximum of three integers
#include <iostream.h>

int maximum( int, int, int );   // function prototype

int main()
{
   int a, b, c, r;
   cout << "Enter three integers: ";
   cin >> a >> b >> c;
   // a, b and c below are arguments to
   // the maximum function call
    r = maximum( a, b, c );
   cout << "The maximum is: " << r  << endl;

   return 0;
}

// Function maximum definition
// x, y and z below are parameters to
// the maximum function definition
int maximum( int x, int y, int z )
{
   int max = x;
   if ( y > max ) max = y;
   if ( z > max ) max = z;
   return max;
}

        Coercion of arguments: promotion of argument type.
            double sqrt(double);
            cout << sqrt(4);
        Warning by compiler of depromotion.

3.7 Header Files

    Header files are text files containing function prototypes .
 
    myheader.h 
       int maximum( int, int, int );   // function prototype
 
    main.cpp

#include <iostream.h>            // < > for system header files
#include "myheader.h"            // " " for programmer defined header files
                                                // The preprocessor copies the contents of the header files here.
                                                // # are preprocessor directives.
int main()
{
   int a, b, c, r;
   cout << "Enter three integers: ";
   cin >> a >> b >> c;
   // a, b and c below are arguments to
   // the maximum function call
    r = maximum( a, b, c );
   cout << "Maximum is: " << r  << endl;

   return 0;
}

maximum.cpp

// Function maximum definition
// x, y and z below are parameters to
// the maximum function definition
int maximum( int x, int y, int z )
{
   int max = x;
   if ( y > max ) max = y;
   if ( z > max ) max = z;
   return max;
}

Creating a VC++ project with multiple source files to find maximum.      
(1) Follow Lab2 to create an empty project as a Win32 Console Application.
(2) Create two new C++ source files: main.cpp and maximum.cpp. Make sure to check "Add to project".
(3) Create a new C/C++ header file: myheader.h.
(4) Cut-and-paste the above code into the corresponding files.

You can always create a new file using any text editor (e.g. Notepad), leave it in the project directory and insert it into the project using Project->Add To Project->Files.
.
The build process:
(1) Preprocessing: the preprocessor copies the code in the header files (.h) into source files (.cpp) and removes all white spaces (.I).
(2) Compilation: the compiler checks for syntax errors and translates source files into binary object code (.obj).
(3) Liking: the linker links the object files and related libraries to form the binary executable (.exe).

System header files are listed in Fig 3.6.

3.8 Random Number Generation
int rand( );                      // random number generator
void srand(int seed);       // further randomize the output of rand() between different runs.
Run the following program a few times to compare the differences between the outputs.

#include <iostream.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i;
   i = rand( );                    // 0 <= i <= RAND_MAX (symbolic constant: 32767).
   cout <<"rand() \t\t" << i << endl;

   i = rand( ) % 6;            // scaling, 0 <= i <= 5.
   cout << "rand() % 6 \t\t" << i << endl;

   i = 1 + rand( ) % 6;      // shift: 1 <= i <= 6.
   cout << "1 + rand() % 6 \t\t" << i << endl;

   unsigned int seed;        // MIN = 0; MAX = 65535.
   cout << "Please enter a seed number :";
   cin >> seed;
   srand(seed);            // seed rand().
   cout <<"\n\n Seeded rand() by "<< seed <<". " << endl;

   i = rand( );                    // 0 <= i <= RAND_MAX (symbolic constant: 32767).
   cout <<"rand() \t\t" << i << endl;

   i = rand( ) % 6;            // scaling, 0 <= i <= 5.
   cout << "rand() % 6 \t\t" << i << endl;

   i = 1 + rand( ) % 6;      // shift: 1 <= i <= 6.
   cout << "1 + rand() % 6 \t\t" << i << endl;

   seed = time(0);              // calendar time in seconds.
   srand(seed);     // seed rand().
   cout <<"\n\n Seeded rand() by "<< seed <<". " << endl;

   i = rand( );                    // 0 <= i <= RAND_MAX (symbolic constant: 32767).
   cout <<"rand() \t\t" << i << endl;

   i = rand( ) % 6;            // scaling, 0 <= i <= 5.
   cout << "rand() % 6 \t\t" << i << endl;

   i = 1 + rand( ) % 6;      // shift: 1 <= i <= 6.
   cout << "1 + rand() % 6 \t\t" << i << endl;
 
    return 0;
}

3.9 "enum"

"enum" creates a user defined type with enumeration constants representing the possible values of the type.

enum Months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
Months x,y;
    x = FEB; y = DEC;
    cout << x;     // 2
    cout << y;     //12

Note:

#include <iostream.h>
int main()
{
enum Months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
Months x;
    for(x=JAN; x <= DEC; x = (Months) (x + 1)) cout << x << endl;
return 0;
}

3.10 Storage Classes

An identifier (a variable or a function) has three attributes: storage class, scope, linkage.
Storage class determines the period during which an identifier exists in memory. (when)
Scope determines where an identifier can be referenced in a program. (where)
Linkage determines which source files an identifier is known. (which file)

Storage Classes: automatic & static

3.11 Scope Rule

        Scope: the portion of the program where an identifier has meaning.
        Five scopes: block scope, function scope, function-prototype scope, file scope, class scope.

        Local variables: variable declared within a scope (block, function, ...).
                                    The are valid only within the scope in which they are declared.
        Global variables: variable declared outside any scope (block, function, ...).
                      The are valid everywhere. They are created at compile time and are never destroyed.
       
        Example:

// Fig. 3.12: fig03_12.cpp
// A scoping example
#include <iostream.h>

void a( void );   // function prototype
void b( void );   // function prototype
void c( void );   // function prototype

int x = 1;      // global variable

int main()
{
   int x = 5;   // variable local to main
   cout << "x local to main: " << x << endl;
   {            // start a new scope 
       cout << "x local to main still valid: " << x << endl;

      int x = 7;  // x local to the block overides the x in main.
      cout << "x local to the block: " << x << endl;
   }            // end of the block, x local to the block destroyed.
   cout << "x llocal to main resumes " << x << endl;
   a();         // a has automatic local x
   b();         // b has static local x
   c();         // c uses global x
   a();         // a reinitializes automatic local x
   b();         // static local x retains its previous value
   c();         // global x also retains its value
   cout << "x local to main: " << x << endl;
   cout << "global x: " << ::x << endl;        // scope resolution operator ::
   return 0;
}

void a( void )
{
   int x = 25;  // initialized each time a is called
   cout << endl << "local x in a is " << x
        << " after entering a" << endl;
   x++;
   cout << "local x in a is " << x
        << " before exiting a" << endl;
}

void b( void )
{
    static int x = 50;  // Static initialization done only when the first time b is invoked.
    cout << endl << "local static x is " << x
         << " on entering b" << endl;
    x++;
    cout << "local static x is " << x
         << " on exiting b" << endl;
}

void c( void )
{
   cout << endl << "global x is " << x
        << " on entering c" << endl;
   x *= 10;
   cout << "global x is " << x << " on exiting c" << endl;
}
 

        Summary: Linkages: determine which source files an identifier is known.
two types: external and internal

External linkage links a global variable defined in another source file and allocates no additional storage.
keyword: extern

Internal linkage forces a global variable only valid within the source file it is defined.
keyword: static

Examples:
1.  In source file F1, we define the global variable x outside all functions as:
   int x;  // x is a global variable that can be externally linked in other source files;
             // storage allocation and initialization occurs when F1 is loaded.

2.  In source file F2, we declare the variable  as:
  extern int x;  // x is an external linkage, no storage is allocated within F2;
                        //x “refers” to a memory location allocated by another module.

3.  In source file F1, we define the global variable y outside all functions as:
   static int y;  // y is a global variable that cannot be externally linked, it is an internal linkage;
                       // storage allocation and initialization occurs when F1 is loaded.

4.  In source file F2, we cannot declare the external linkage to y as:
   extern int y;  // Error, y is an internal linkage in F1.

3.12 Recursion
 
    A recursive function is a function that calls itself either directly or indirectly through another function.
    e.g. compute the factorial of 3.

3! = 3*2*1
2! = 2*1
3! = 3 * 2!

n! = n * (n-1)!
1! = 1
0! = 1

#include <iostream.h>
unsigned long factorial( unsigned long );
int main()
{
   cout << "3! = " << factorial( 3 ) << endl;
   return 0;
}

// Recursive definition of function factorial
unsigned long factorial( unsigned long n )
{ unsigned long pf;

   if ( n <= 1 )  // base case
      pf = 1;
   else                // recursive case
      pf =  n * factorial( n - 1 );
 
  return pf;
}

_____________________________________________________

            Key: Every time a recursive function is invoked, the compiler makes a new activation record for the function.

            First call to factorial() from main: n = 3
                        pf = 3 * factorial (2);            // pf = 3 * 2;
            Second call to factorial() from factorial(): n = 2
                        pf = 2 * factorial (1);            // pf = 2 * 1
            Third call to factorial() from factorial(): n = 1
                       pf = 1;

        Summary of Recursion:

3.16 Inline Functions
        Copy the source code to where it is called at the compile time, to make the program run faster.
// Using an inline function to calculate the volume of a cube.
#include <iostream.h>

inline float cube(float f );

int main()
{
   cout << "Enter the side length of your cube:  ";
   float side;

   cin >> side;
   cout << "Volume of cube with side "
        << side << " is " << cube( side ) << endl;
 
   cout << "Enter the side length of your cube:  ";
   cin >> side;
   cout << "Volume of cube with side "
        << side << " is " << cube( side ) << endl;
   
return 0;
}
 
 float cube(float s ) { return s * s * s; }
 

     Summary of inline functions: 3.17 References and Reference Parameters
        A reference is an alias of another variable. The use of a reference is the same as using the variable.
        #include <iostream.h>
int main()
{
   int x = 3;
   int &y = x;  // y is a reference to x or an alias for x. (y is x).
   cout << "x = " << x << endl << "y = " << y << endl;
   y = 7;
   cout << "x = " << x << endl << "y = " << y << endl;
   x = 8;
   cout << "x = " << x << endl << "y = " << y << endl;
   return 0;
}
    Call-by-value: the value of the argument is passed to the called function.
    Call-by-reference: the reference of the argument is passed to the called function.
    e.g.:
// Comparing call-by-value and call-by-reference
#include <iostream.h>

int squareByValue( int x);
void squareByReference( int & y);
 
int main()
{
   int x = 2, z = 2;

   cout << "x = " << x << " before squareByValue\n";
   cout << "Value returned by squareByValue: " << squareByValue( x ) << endl
   cout << "x = " << x << " after squareByValue\n" << endl;

   cout << "z = " << z << " before squareByReference" << endl;
   squareByReference( z );
   cout << "z = " << z << " after squareByReference" << endl;

   return 0;
}

int squareByValue( int a )
{
   return a *= a;   // caller's argument not modified
}

void squareByReference( int &cRef )
{
   cRef *= cRef;    // caller's argument modified
}
 

    Summary of References 3.18 Default Arguments
// Using default arguments
#include <iostream.h>

// int boxVolume( int length = 1, int width = 1, int height = 1 );
int boxVolume( int length, int width = 1, int height = 1 );

int main()
{
   cout << "The default box volume is: " << boxVolume()    // (1,1,1)
      << "\n\nThe volume of a box with length 10,\n"
        << "width 1 and height 1 is: " << boxVolume( 10 )        //(10,1,1)
        << "\n\nThe volume of a box with length 10,\n"
        << "width 5 and height 1 is: " << boxVolume( 10, 5 )    //(10,5,1)
        << "\n\nThe volume of a box with length 10,\n"
        << "width 5 and height 2 is: " << boxVolume( 10, 5, 2 )
        << endl;

   return 0;
}

// Calculate the volume of a box
int boxVolume( int length, int width, int height )
{
   return length * width * height;
}
 

3.20 Function Overloading

      Functions of the same name have different sets of parameters.

// Using overloaded functions
#include <iostream.h>

// function prototypes are omitted since functions are defined before usage.
int square( int x ) { return x * x; }                  //@square$qi
double square( double x ) { return x * x; }    //@square$qd

int main()
{
   cout << "The square of integer 7 is " << square( 7 )
        << "\nThe square of double 7.5 is " << square( 7.5 )
        << endl;
   return 0;
}

    Summary of Function Overloading
  • Functions of the same name have different sets of parameters.
  • Compilers treat overloaded functions as different functions and identify them using name mangling (function names are encoded by their parameter types and the number of parameters).
  • Return type is not used in name mangling.
  • Function name is overloaded, it is used by different functions.
  • 3.21 Function Templates

        Parameterized type selection.

    // Using a function template
    #include <iostream.h>

    template < class T >        // T is the type parameter and is replaced at each function call.
    T maximum( T value1, T value2, T value3 )
    {
       T max = value1;
       if ( value2 > max )
          max = value2;
       if ( value3 > max )
          max = value3;
       return max;
    }

    int main()
    {
       int int1, int2, int3;

       cout << "Input three integer values: ";
       cin >> int1 >> int2 >> int3;
       cout << "The maximum integer value is: "
            << maximum( int1, int2, int3 );          // int version

       double double1, double2, double3;

       cout << "\nInput three double values: ";
       cin >> double1 >> double2 >> double3;
       cout << "The maximum double value is: "
            << maximum( double1, double2, double3 ); // double version

       char char1, char2, char3;

       cout << "\nInput three characters: ";
       cin >> char1 >> char2 >> char3;
       cout << "The maximum character value is: "
            << maximum( char1, char2, char3 )        // char version
            << endl;

       return 0;
    }

        Summary of Template Functions



    Problem Solving Strategies
    (1) Understand the problem statements fully.
    (2) Design a solution for the problem using pseudo code.
            (2.a) Modify a similar solution if there is one.
            (2.b) Or, write a new solution.
    (3) Implement the solution using C++.
    (4) Hand trace the source code with imaginary input data.
    (5) Fix logical errors.
    (6) Compile and fix syntax errors.
    (7) Test the program with different input data values.
    (8) Hand-trace the source code with the input data that breaks the program and fix the problems found.
    (9) Use cout to display variable values to help the hand trace.
    (10) Keep a record of all the mistakes that you have made in the program.


        Multiple source files and IDE projects
        Global variables and external links

    Three steps of  "compilation".
    (1) Preprocessing:

    (2) Compiling: (3) Linking:

    More on the three attributes of a variable: storage class, scope, linkage.