CS (151) COURSE - INTRODUCTION TO PROGRAMMING C++

CS (151) - INTRODUCTION TO PROGRAMMING C++

Teacher: Dr. Shrestha



Fall Semester, 2000

Class hour: 5:00 - 10:00 Monday

Materials for November 20, 2000
*****************************

Chapter (3) (Contd.)
********************

FUNCTIONS


Save days' work in your diskette !

    PROMOTION HIERARCHY FOR BUILT-IN DATA TYPES

    One of the important feature of function prototypes is the coercion of arguments, i.e. the forcing of arguments to the appropriate type. For example, the math library function sqrt can be called with an integer argument even though the function prototype in math.h specifies a double argument, and the function will still work correctly. The promotion rules based on the hierarchy below shows how types can be converted to other types without losing data. Lower to higher data type in the hierarchy is allowed but higher to lower not.

    long double
    The type long double is a floating - point type much like double but with much greater magnitude.

    double
    The type double is a floating-point type much like float, but a variable of type double can store a value of much greater magnitude with greater precision than float.

    float
    Used for specifying real numbers, i.e. numbers with decimal points.

    unsigned long int (unsigned long)
    The C++ language specification requires that a variable of type unsigned long int or in short, unsigned long is stored in at least 4 bytes (32 bits), and thus, may hold a value at least in the range 0 to 4294967295.

    long int (long)
    This type of data is stored in at least 4 bytes and may hold a value in the range of +/- 2147483647.

    unsigned int (unsigned)
    Unsigned int can have only positive values e.g. 50, 2034 etc.

    int
    An int is stored in at least two bytes of memory and can have positive and negative values e.g. -1, 120 etc

    unsigned short int (unsigned short)
    The minimum range of values for this type is 32767.

    short int (short)
    The minimum range of values for short integers is +/- 32767

    unsigned char
    Used for long rows of character.

    short
    Synonomous with short int.

    char
    Used for specifying character data e.g. x, $, 7 etc.

    Header Files

    OLD STYLE HEADER FILES (extension h)

    <assert.h>
    - contains macros and information for adding diagnostics that aid program debugging. The new version of this header file is <cassert>

    <ctype.h>
    - contains function prototypes for functions that test characters for certain properties, and function prototypes for functions that can be used to convert lowercase letters to uppercase letters and vice versa. The new version of this header file is <cctype>

    <float.h>
    - contains the floating point size limits of the system. The new version of this header file is <cfloat>

    <limits.h>
    - contains the integral size limits of the system. The new version of this file is <climits>

    <math.h>
    - contains function prototypes for math library functions. The new version of this header file is <cmath>

    <stdio.h>
    - contains function prototypes for the standard input/output library functions and information used by them. The new version of this header file is <cstdio>

    <stdlib.h>
    - contains function prototypes for conversions of numbers to text, text to numbers, memory allocation, random numbers, and various other utility functions. The new version of this header file is <cstdlib>

    <string.h>
    - contains function prototypes for C-style string processing functions. The new version of this header file is <cstring>

    <time.h>
    - contains function prototypes and types for manipulating the time and date. The new version of this header file is <ctime>

    <iostream.h>
    - contains function prototypes for the standard input and standard output functions. The new version of this header file is <iostream>

    <iomanip.h>
    - contains function protypes for the stream manipulator that enable formatting of streams of data. The new version of this header file is <iomanip>

    <fstream.h>
    - contains function prototypes for functions that perform input from files on disk and ouput to files on disk. The new version of this file is <fstream>

    NEW STYLE HEADER FILES

    <utility>
    - contains classes and functions that are used by many standard library header files

    <vector>, <list>, <deque>, <queue>, <stack>, <map>, <set>, <bitset>
    - contain classes that implement the standard library containers. Containers are used to store data during a program's execution

    <functional>
    - contains classes and functions used by algorithms of the standard library

    <memory>
    - contains classes and functions used by the standard library to allocate memory to the standard library containers.

    <iterator>
    - contains classes for manipulating data in the standard library containers.

    <algorithm>
    - contains functions for manipulating data in the standard library containers.

    <exception>, <stdexcept>
    - contains classes that are for exception handling

    <string>
    - contains the definition of class string from the standard library.

    <sstream>
    - contains function prototypes for functions that perform input from strings in memory and output to strings in memory.

    <locale>
    - contains classes and functions normally used by stream processing to process data in the natural form for different languages.

    <limits>
    - contains a class for defining the numerical data type limits on each computer platform.

    <typeinfo>
    - contains classes for run-time type identification (determining data types at execution time).

    PROGRAMMER-DEFINED HEADER FILES

    The programmer can create header files. These files should have an extension .h A programmer-defined header file can be included by using the #include preprocessor directive.

    Random Number Generation
    Element of chance- random number rand function in the standard library

    i = rand();

    The rand function generates an integer between 0 and RAND_MAX (some maximum number defined in the library e.g. 32767). If rand truly produces integers at random, every number between 0 and RAND_MAX has an equal chance or probability of being chosen each time rand is called.The function prototype for the rand is in the library, <stdlib.h>.

    SCALING
    Sometimes we need to produce numbers in a range different than what rand produces. In such a case, we scale the range as shown below:
    rand() % 6
    produces integers in the range 0 to 5. Here, the number 6 is called the scaling factor. We can also shift the range of the numbers produced by adding another number e.g. adding 1 to make the range from 1 to 6.

    EXAMPLE
    Let us develop a program to simulate 6000 rolls of a six-sided die and print the value of each roll. If rand does what it is supposed to do, each integer from 1 to 6 should appear approximately 1000 times.

    //Roll a six-sided die 6000 times
    #include <iostream.h>
    #include <iomanip.h>
    #include <stdlib.h>
    
    int main()
    {
         int     frequency1 = 0, frequency2 = 0,
    	     frequecny3 = 0, frequency4 = 0,
    	     frequency5 = 0, frequency6 = 0,
    	     face;
    
         for ( int roll = 1; roll <=6000; roll++ ) {
    	face = 1 + rand() % 6;
    
    	switch ( face ) {
    		case 1:
    			++frequecny1;
    			break;
    		case 2:
    			++frequency2;
    			break;
    		case 3:
    			++frequency3;
    			break;
    		case 4:
    			++frequency4;
    			break;
    		case 5:
    			++frequency5;
    			break;
    		case 6:
    			++frequency6;
    			break;
    		default:
    	   //The program should never get to the
    	   //default because one of the face
    	   //will certainly show up. But it is
    	   //a good practice to put default.
    
    		  cout << "should never get here!";
    	}
    }
    
    cout 	<< "Face"  << setw( 13 ) << "Frequency"
    	<< "\n	1" << setw( 13 ) << frequency1
    	<< "\n	2" << setw( 13 ) << frequency2
    	<< "\n	3" << setw( 13 ) << frequency3
    	<< "\n	4" << setw( 13 ) << frequency4
    	<< "\n	5" << setw( 13 ) << frequency5
    	<< "\n	6" << setw( 13 ) << frequency6
    	<< endl;
    return 0;
    }
    

    Provide a default case to catch errors even if you are certain that you have no bugs.

    The rand function actually generates pseudo-random numbers i.e., it repeats the same sequence of random numbers each time it is called. It can be conditioned to produce a different sequence of random numbers for each execution which is called randomizing, and is done with the standard library function srand. The srand function takes an unassigned integer argument and seeds the rand function to produce a different sequence of random numbers for each execution of the program. The function prototype for the srand function is in the header file <stdlib.h>.

    EXAMPLE

    //Randomizing die-rolling program
    #include <iostream.h>
    #include <iomanip.h>
    #include <stdlib.h>
    
    int main()
    {
    	unsigned seed;
    
    	cout << "Enter seed: ";
    	cin >> seed;
    	srand( seed );
    
    	for ( int i =1; i <=10; i++ ) {
    	   cout << setw( 10 ) << 1 + rand() % 6;
    
    	   if ( i % 5 == 0 )
    		cout << endl;
    	}
    	return 0;
    } 
    

    If we wish to randomize without the need for entering a seed each time, we may use a statement like

    srand( time( 0 ) );

    This causes the computer to read its clock to obtain the value for the seed automatically. The time function (with the argument 0) returns the current "calendar time" in seconds. This value is converted to an unsigned integer and used as the seed to the random number generator. The function prototype for time is in the header file <time.h>.

    STORAGE CLASSES

    C++ provides 4 storage class specifiers:
    AUTO, REGISTER, EXTERN, and STATIC
    An identifier's storage class specifier helps determine its storage class, scope, and linkage.

    Storage class - determines the period during which that identifier exists in memory

    Scope - determines if the identifier can be referenced in a program

    Linkage - determines for a mulitple-source-file program whether an identifier is known only in the current source file or in any source file with proper declarations.

    Automatic storage class

    The auto and register keywords are used to declare variables of the automatic storage class. Such variables are created when the block in which they are declared is entered, they exist while the block is active, and they are destroyed when the block is exited.

    Example:
    auto float x, y;
    declares that float variables x and y are local variables of automatic storage class, they exist only in the body of the function in which the definition appears.

    register int counter = 1;
    declares that the integer variable counter be placed in one of the computer's register and be initialized to 1.

    Either write auto or register but not both to an identifier.

    Static Storage Class

    The keywords extern and static are used to declare identifiers for variables and functions of the static storage class. Such variables exist from the point at which the program begins execution.

    There are two types of identifiers with static storage class: external identifiers (such as global variables and function names) and local variables declared with the storage class specifier static. Global variables and function names default to storage class specifier extern. Global variables are created by placing variable declarations outside any function definition. They retain their values throughout the execution of the program.

    SCOPE RULES

    The portion of the program where an identifier has meaning is known as its scope.

    VARIOUS TYPES OF SCOPES

    FUNCTION SCOPE
    Labels (identifiers followed by a colon such as start:) are the only identifiers with function scope. Labels can be used anywhere in the function in which they appear, but cannot be referenced outside the function body. Labels are used in switch structures (as case labels).

    FILE SCOPE
    An identifier declared outside any function has file scope. Such an identifier is known in all functions from the point at which the identifier is declared until the end of the file. Global variables have file scope.

    BLOCK SCOPE
    Identifiers declared inside a block have a block scope. Block scope begins at the identifier's declaration and ends at the temininating right brace (}) of the block. Local variables declared at the beginning of a function have block scope as do function parameters.

    FUNCTION-PROTOTYPE SCOPE
    The identifiers used in the parameter list of a function prototype have function-prototype scope. Function prototypes do not require names in the parameter list - only types are required.

    Example to illustrate various types of scopes

    // A scoping example
    #inlcude <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;	// local variable to main
      cout	<<"local x in outer scope of main is "
         	<< x << endl;
    //5, global value 1 of x is hidden in main.
      {		// start new scope
    	int x = 7;
    	cout <<"local x in inner scope of main is "
    	     << x << endl;
    //7, reinitialized in next block, so hid old value.
      }		// end new scope
    
      cout 	<<"local x in outer scope of main is " 
    	<< x << endl;
    //5, value 7 of x destroyed when the block is exited
    
    // Program defines 3 functions a, b, and c - each 
    // takes no arguments and returns nothing. Function a 
    // defines automatic variable x and initializes it to 
    // 25. When a is called, the variable is printed, 
    // incremented, and printed again before exiting the 
    // function. Each time this function is called, 
    // automatic variable x is recreated and initialized 
    // to 25.
    
      a();	// a has automatic local x
    
    // Function b declares static variable x and 
    // initializes it to 50. Local variables declared as 
    // static retain their values even when they are out 
    // of scope. When b is called, x is printed, 
    // incremented, and printed again before exiting the 
    // function. In the next call to this function, 
    // static local variable x will contain the value 51.
    
      b();	// b has static local x
    
    // Function c does not declare any variables. 
    // Therefore, when it refers to variable x, the global 
    // x is used. When c is called, the global variable is 
    // printed, mulitplied by 10, and printed again before 
    // exiting the function. The next time function c is 
    // called, the global variable has its modified value, 
    // 10.
    
      c();	// c uses global x
    
    // Finally, the program prints the local variable x 
    // in main again to show that none of the function 
    // calls modified the value of x because the functions 
    // all referred to variables in other scopes.
    
      a();	// a reinitializes automatic local x
      b();	// static local retains its previous value
      c();	// global x retains it value
    
    
      cout  <<"local x in main is "
          	<< x << endl;
      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;
    //25
      cout	<<"local x in a is " << x
    	<<"before exiting a" << endl;
    //26
    }
    
    void b( void )
    {
      static int x = 50; //Static initialization
    		     //only first time b is
    		     //called
      cout  << endl <<"local static x is " << x
    	<< "on entering b" << endl;
    	++x;
    //50
      cout  << "local static x is " << x
    	<<"on exiting b " << endl;
    //51
    }
    
    void c( void )
    {
      cout	<< endl <<"global x is " << x
    	<< "on entering c " << endl;
      x+=10;
    //1
      cout <<"global; x is " << x
    	<<"on exiting c " << endl;
    //10
    }
    

    RECURSION
    A recursive function is a function that calls itself either directly or indirectly through another function.

    Example

    // Recursive Factorial function
    
    #include 
    #include 
    
    unsigned long factorial( unsigned long );
    
    int main()
    {
      for ( int i = 0; i <= 10; i++ )
         cout << setw( 2 ) << i << "! = "
    	  << factorial( i ) << endl;
    
      return 0;
    }
    
    // Recursive definition of function factorial
    //unsigned long factorial( unsigned long number )
    {
      if( number <= 1) // base case
        return 1;
      else
        return number * factorial( number - 1 );
    // This line is recursive.
    }
    

    THE FIBONACCI SERIES
    The Fibonacci series
    0, 1, 1, 2, 3, 5, 8, 13, 21...
    begins with 0 and 1 and has a property that each subsequent Fibonacci number is the sum of the previous two Fibonacci numbers.

    The Fibonacci series occurs in nature and, in particular, describes a form of spiral. The ratio of successive Fibonacci numbers converges to a constant value of 1.618.. This number, also, repeatedly occurs in nature and has been called the GOLDEN RATIO or the GOLDEN MEAN. People find the GOLDEN MEAN aesthetically pleasing. Architects often design windows, rooms, and buildings whose length and width are in the ratio of the golden mean. Postcards are often designed with a golden mean length/width ratio.

    The Fibonacci series may be defined recursively as follows:

    fibonacci( 0 ) = 0
    fibonacci( 1 ) = 1
    fibonacci( n ) = fibonacci( n-1 ) + fibonacci( n-2 )

    Example of Fibonacci C++ Program

    //Recursive Fibonacci Function
    
    #include <iostream.h>
    
    long fibonacci( long );
    
    int main()
    {
      long result, number;
    
      cout 	<< "Enter an integer: ";
      cin 	>> number;
      result = fibonacci( number );
      cout 	<< "Fibonacci(" << number << ") = "
    	<< result << endl;
      return 0;
    }
    
    //Recursive definition of function fibonacci
    
    long fibonacci( long n )
    {
       if ( n == 0 || n == 1 ) // base case
          return n;
       else
          return fibonacci( n-1 ) + fibonacci( n-2 );
    }
    

    The number of calls in these kind of recursive program rapidly gets out of hand. So we need to be careful not to cause an exponential explosion of call, often called as EXPONENTIAL COMPLEXITY.

    RECURSION vs. ITERATION
    Any problem that can be solved recursively can also be solved iteratively. A recursive approach is normally chosen in preference to an iterative approach when the recursive approach more naturally mirrors the problem and results in a program that is easier to understand and debug.

    FUNCTIONS WITH EMPTY PARAMETER LISTS

    In C++, an empty parameter list is specified by writing either void or nothing at all in parentheses.
    Example:
    void print();
    void function1();
    void function( void );

    INLINE FUNCTIONS
    C++ provides inline functions to help reduce function-call overhead, especially for small functions. With the use of inline function, the compiler generates a copy of the function's code in place to avoid a function call. Hence, using inline functions can reduce execution time, but can increase program size.

    Example

    //Using an inline function to calculate the
    //volume of a cube.
    
    #include 
    
    inline float cube( const float s ) {
       return s * s * s; }
    
    //The keyword const in the parameter 
    //list tells the compiler that the function 
    //does not modify variable s.
    
    int main()
    {
       cout << "Enter the side of the cube: ";
    
       float side;
     
       cin >> side;
       cout << "Volume of cube with side "
    	<< side
    	<< " is " << cube( side )
    	<< endl;
       return 0;
    }
    

    REFERENCES AND REFERENCE PARAMETERS

    Two ways to invoke functions:
    (1)call-by-value
    - a copy of the argument's value is made and passed to the called function. Changes to the copy do not affect the original variable's value in the caller.
    (2)call-by-reference
    - the caller gives the called function the ability to directly access the caller's data, and to modify that data if the called function so choses.

    DEFAULT ARGUMENTS

    Function calls may commonly pass a particular value of an argument. The programmer can specify that such an argument is a default argument, and the programmer can provide a default value for that argument.

    UNARY SCOPE RESOLUTION OPERATOR ( :: )

    This allows an access to a global variable when a local variable of the same name is in scope.

    Example

    //Using the unary scope resolution operator
    //To emphasize that the local and global //versions of constant variable PI are distinct, //the program declares one of the variables //double and the other float. #include <iostream.h> #include <iomanip.h> const double PI = 3.14159265358979; int main() { const float PI = static_cast< float >( ::PI ); cout << setprecision( 20 ) << " Local float value of PI = " << PI // This print 3.14159. << "\nGlobal double value of PI = " << ::PI << endl; // This prints 3.14159265358979. return 0; }

    FUNCTION OVERLOADING

    C++ enables several functions of the same name to be defined as long as these functions have different sets of parameters. This capability is called function overloading. Function overloading is commonly used to create several functions of the same name that perform similar tasks, but on different data types. Overloading functions that perform closely related tasks can make programs more readable and understandable.

    Overloaded functions are distinguished by their signatures. A signature is a combination of a function's name and its parameter types. The compiler encodes each function identifier with the number and types of its parameters. This is called name mangling or name decoration.

    FUNCTION TEMPLATES

    Function templates enable the creation of functions that perform the same operations on different types of data, but the function template is defined only once.

    Example

    //Using a function template
    
    #include <iostream.h>
    
    template < class T >
    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 );
    
      double double1, double2, double3;
    
      cout << "\nInput three double values; ";
      cin  >> double1 >> double2 >> double3;
      cout << "The maximum double value is: "
           << maximum( double1, double2, double3 );
    
      char char1, char2, char3;
    
      cout 	<< "\nInput three characters: ";
      cin 	>> char1 >> char2 >> char3;
      cout	<< "The maximum character value is: "
    	<< maximum( char1, char2, char3 );
    	<< endl;
    // The maximum for characters are evaluated by
    // their place, e.g, C is maximum among A, C, and B.
    
      return 0;
    }
    

    CHAPTER (4) - ARRAYS
    ************************

    An array is a consecutive group of memory locations that all have the same name and same type.

    Example:
    c[0] = 3
    c[1] = 10
    c[2] = -5
    c[3] = 21

    Here c is an integer array containing 4 elements. Any one of these elements may be referred to by giving the name of the array followed by the position number of the particular element in square brackets. The position number contained within the square bracket is called a SUBSCRIPT.


THANKS FOR STOPPING BY !

THINGS CHANGE - COME BACK AGAIN !

TAKE CARE !

AFTER TODAY'S CLASS IS OVER, LOOK AT THE ASSIGNMENT.