Robotics C++ Physics II AP Physics B Electronics Java Astronomy Other Courses Summer Session  

Functions

 

Writing Functions

Prototypes

Passing Parameters

Recursion

Suggestions

Returning Values

Scope of Identifiers

By Reference and By Value

Summary

Exercises

 

Programs of any significant size are broken down into logical pieces called functions.  It was recognized long ago that programming is best done in small sections which are connected in very specific and formal ways.  C++ provides the construct of a function, allowing the programmer to develop new functions not provided in the original C++ libraries.  Breaking down a program into blocks or sections leads to another programming issue regarding identifier scope.  Also, functions need to communicate with other parts of a program which requires the mechanics of parameter lists and a return value.

Writing Functions in C++

A function is like a box which takes data in, solves a problem, and usually returns a value. 
    The standard math functions follow this pattern:
    sqrt (2)  --->  1.414 
     There are times when the built-in functions of C++ will not get the job done.  We will often need to write
     customized functions which solve a problem using the basic tools of a programming language.

 

     For example, suppose we need a program which converts gallons into liters.  We could
    solve the problem within function main.
 
    #include <iostream.h>
   
    int main ()
    {
        double    gallons, liters;
 
        cout << "Enter an amount of gallons ---> ";
        cin >> gallons;
        liters = gallons * 3.785;
        cout << "Corresponding amount in liters = " << liters << endl;
        return 0;
    }

 
This works fine, but the mathematics of the conversion is buried inside function main.  The conversion tool is not available for general use.  We are not following the software engineering principle of writing code which can be recycled in other programs.
 
Here is the same routine coded as a reusable function:
 
 #include <iostream>            //note .h omitted, must now include next line

 

 using namespace std;          //can use either approach in this course 
 
double  convert (double amount);     //  function prototype - name amount optional
 
int main ()
{
        double   gallons, liters;
        cout << "Enter an amount of gallons ---> ";
        cin >> gallons;
        liters = convert (gallons);
        cout << "Corresponding amount in liters = " << liters << endl;
        return 0;
}
 
double  convert (double  amount)              // function definition
{
        return   amount * 3.785;    // convert (above) becomes amount*3.785
}
 
   Sample run output:
 
   Enter an amount of gallons ---> 10
   Corresponding amount in liters = 37.85

     
Here is the sequence of events in this short program. 
 
ð Execution begins in function main with the user prompt and the input of an amount of gallons.
ð The function convert is called and the amount of gallons is passed as an argument to function convert.
ð Program execution moves to function convert which solves the math and returns the answer to the calling statement.
ð The answer is printed out.
 
The general syntax of a function declaration is
 
      return type     name of function    (parameter list)
 
ð The return type must be a data type already recognized by the compiler.
ð The name of the function must be a valid identifier.
ð The parameter list consists of one or more type-identifier pairs of information. 
ð These parameters are called the formal parameters.
 
The line above function main
 
       double  convert (double);            //  function prototype
 
ð Is called the function declaration or prototype. 
ð This statement declares the function before it is referred to in function main. 
ð When the compiler encounters the line inside of function main
 
        liters = convert (gallons);
 
it has already seen convert defined in the prototype.  The function definition at the bottom of the program completes the function which must include both the prototype and code which solves the problem. 
 
We could have placed the function definition above function main and omitted the function declaration, but this is good practice for later issues regarding program design in C++.
 
ð When writing the function prototype, the identifier of a variable type in the parameter list can be omitted. 
ð  The compiler will ignore any identifier names in the parameter list of a function prototype. 
 
ð When the function is called, the value passed to the function is called the actual parameter. 
ð The integer named gallons inside of function main serves as the actual parameter.

Returning Values

 
ð The values passed to a function become the source of input for the function. 
ð Notice that inside of function convert, no data input was required from an external source.
 
ð The single parameter in the above program is an example of a value parameter. 
 
A value parameter has the following characteristics:
 
ð It receives a copy of the argument which was passed to the function. 
ð The value of 10 stored in gallons (inside of main) is passed to the parameter amount (inside of convert).
ð This value parameter is a variable which can be modified within the function.
ð This value parameter is a local variable. 
ð This means that it is valid only inside of the block in which it is declared. 
ð We refer to a function in C++ as a block of code.
 
 A function can return a single value. 
 
ð If this data type is omitted the default value returned is an integer. 
ð Somewhere in the body of the function there must be a return statement if we want the function to return the correct answer.
 
If a function returns no value the term void should be used.  For example:
 
void  printHello ( )
{
        cout << "Hello world" << endl;
}
 
ð The term void can be used in one or both locations of input and output for the function. 
ð However, an alternative to a void parameter list is the empty parameter list as used in the above example.
 
A function can have multiple parameters in its parameter list.  For example:
 
double  doMath (int  a, double x)
{
        ... code ...
}
 
When this function is called, the arguments fed to function doMath must be of an appropriate type.  The first argument must be an integer.  The second argument can be an integer (which will be promoted to a double),
but it will most likely be a double.
 
    doMath (2,3.5);                     //  this is okay
    doMath (1.05, 6.37);             //  this will not compile
 
Value parameters are often described as one-way parameters. 
The information flows into a function but no information is passed back through the value parameters. 
A single value can be passed back using the return statement, but the actual parameters in the function remain unchanged.
 
The actual arguments used to supply values for the value parameters can be either literal values (2, 3.5) or variables (a,x).
 
     doMath (a,x);               // example using variables
 

Scope of Identifiers

 

The following concepts are very important in all modern CS languages

 

ð Modularization: Breaking the code into components or functions (and classes covered later)

ð Information Hiding: Restricting where variables and functions have meaning - scope

Scope

 

There are four categories of scope in C++:  file scope, block scope, function scope, and function-prototype scope.
 
ð File Scope
 
An identifier declared outside of any function has file scope.  Such an identifier can be used in all functions which follow until the end of the file.  Such variables are also called global variables. 
 
The function prototypes declared near the top of a source code have file scope. Global constants are often used in programs but global variables are usually inappropriate.  The use of global variables can lead to difficult bugs.  
 
ð Block Scope
 
 Block Scope applies to identifiers declared inside a block. 
A block begins wherever that identifier is declared until the terminating right brace (}) of that block. 
Functions have two possible categories of identifiers with block scope.

    Function parameters have block scope.  The local identifiers used as parameters are known throughout the function

    Local variables declared inside the function have block scope, from the point at which they are defined.
    If a block is nested inside another block, identifiers declared in the inner block only have meaning inside the inner block. 
      The outer block does not have access to the inner identifiers.  For example:
     
      #include <iostream>
        using namespace std;
 
       int main ()
        {
            int  a = 3;
 
            cout << "The value of a in the outer block = " << a << endl;
            {
                int  a = 8;
       
                cout << "The value of a in the inner block = " << a << endl;
            }
            cout << "The value of a in the outer block is still " << a << endl;
            return 0;
       }
 
 
 
Identifiers used in the parameter list of a function prototype have no meaning elsewhere in the program, even in the definition of the function.  Identifiers used in the parameter list of a function prototype are ignored by the compiler.

Passing By Reference vs By Value

 
#include <iostream>
using namespace std;

int Mystery1 (int x);            //need type, the name of the variable, x, is optional here
void Mystery2 (int & y);

int main ( )
{
    int mon = 2;
    int tue = 3;
    int wed = 0;
      cout << "original mon: "<<mon<<endl;
      cout << "original tue: "<<tue<<endl;
      cout << "original wed: "<<wed<<endl;
      cout <<endl;
    wed = Mystery1 (mon);                                //Call Mystery1, pass mon
    Mystery2 (tue);                                           //Call Mystery2, pass tue
    cout << "mon is now: "<<mon<<endl;
    cout << "wed is now: "<<wed<<endl;
    cout << "tue is now: "<<tue<<endl;
    cout << endl;

    return 0;
}

int Mystery1 (int x)
{
    return 2*x;
}

void Mystery2 (int & y)
{
    y = 10*y;
}
 

 

Prototypes

You can omit prototypes if the function is defined before it is used.

 

Always provide prototypes, however, because this avoids typing code to the order in which functions are defined (the order can easily change later)

Example 1

#include <iostream>
using namespace std;

void testSomething ( );            //prototype required for this function

int main()
{
    testSomething ( );
    return 0;
}
void printHi ( )
{
    cout << "No function protype required-function defined before used"<<endl;
}
void testSomething ( )
{
    printHi ( );
}
 

Example 2

#include <iostream>
using namespace std;

//no function prototypes required here

void printHi ( )
{
    cout << "No function protype required-function defined before used"<<endl;
}
void testSomething ( )
{
    printHi ( );
}

int main() //all functions before main - bad paractice!
{
    testSomething ( );
    return 0;
}

Passing Parameters

#include <iostream.h>                     //preprocessor directive
 

//function prototypes
void Greetinga ( );                            //no parameters are passed, nothing returned

void Greetingb (int);                         //an integer parameter is passed, nothing returned
double Matha (int a, double b);      //int and double parameters passed, a and b ignored, double returned

int main ( )
{
    Greetinga ( );                                 //calling function Greetinga
    int a = 7;
    Greetingb(a);                                 //calling function Greetingb
    int b = 2;
    double c = 4.0;
    double answer;
    answer = Matha(b, c);                 //calling function Matha, return placed in answer
    cout <<"Return from Matha is "<<answer<<endl;
    return 0;
}
 

//function definintion
void Greetinga ( )
{
    cout <<"Good morning"<<endl;
}

//function definition

void Greetingb (int m)
{
    cout <<"You are customer number "<<m<<endl;
}
 

//function definition
double Matha(int a, double b)
{
    return a/3.0 + b;
}


 

 

Function Calling Another

#include <iostream.h>

void firstFunction (int);
void secondFunction ();

int main ( )
{
    int age = 0;
    cout << "Please enter an age"<<endl;
    cin >>age;
    firstFunction (age);
    return 0;
}
void firstFunction (int age)
{
    if (age < 20)
        secondFunction ( );
    else
        cout <<"age is not less than 20"<<endl;
}
void secondFunction ( )
{
    cout << "age is less than 20"<<endl;
}

 

Note: Always check the possibilities when debugging code

Suggested Approach

Step 1: Outline the Program with Function Prototypes

Step 2: Copy and Paste Function Prototypes

Step 3: Complete the Main, Then Complete the Other Functions

Step 1: Outline the Program by Writing the Following. Here, the main modules are identified.

//Name
//Date
//Lab Number

#include <iostream.h>

void firstFunction (int a, double b);            //a and b not required. My rationale for including them
int secondFunction (int c);                           //c not required
double thirdFunction (double d, int e);     //d and e not required

int main ( )
{
    return 0;
}

Step 2: Copy and Paste the function prototypes below the main, remove semicolon, etc.

Run this "Stub". If you have errors here, correct them before filling in the function details

//Name
//Date
//Lab Number

#include <iostream.h>

void firstFunction (int a, double b);
int secondFunction (int c);
double thirdFunction (double d, int e);

int main ( )
{
    return 0;
}

void firstFunction (int a, double b)
{
}
int secondFunction (int)
{
    return 1;                                            //1 is a placeholder so the program will run
}
double thirdFunction (double, int)
{
    return 1.0;                                        //1.0 is a placeholder so the program will run
}


Step 3: Initialize variables in the main function and call the other functions

//Name
//Date
//Lab Number

#include <iostream.h>

void firstFunction (int a, double b);
int secondFunction (int c);
double thirdFunction (double d, int e);

int main ( )
{

    //all variables declared and intitialized at the beginning of function
    int a = 6;
    double b = 10.0;
    int c = 32;
    double d = 4.5;
    int e = 5;
    int secondResponse = 0;
    double thirdResponse = 0.0;


    //Call the functions. Note that data type not included - stated already.

    firstFunction (a,b);
    secondResponse = secondFunction (c);
    thirdResponse = thirdFunction (d, e);
 

    //Print the output of second and third functions
    cout << "c is: "<<c<<" and 2 times c is: "<<secondResponse<<endl;
    cout << "d is: "<<d<<" e is: "<<e<<" and d + e is: "<<thirdResponse <<endl;

    return 0;
}

//Function definition

void firstFunction (int a, double b)
{
    cout <<"a is: "<<a<<endl;
    cout <<"b is: "<<b<<endl;
}

 

//Function definition
int secondFunction (int c)
{
    return 2*c;                                        //The function in main now becomes 2*c
}

 

//Function definition
double thirdFunction (double d, int e)
{
    return d + e;                                    //The function in main now becomes d + e
}


 

Some Points

 

Prototypes

 

ð Must include return type, name of function, data type of parameters

ð Name of parameters is optional

 

Main

 

ð Declare and initialize variables before they are used.

ð A good rule is to declare and initialize variables at the beginning of the main.

ð Call functions by invoking the name and including the parameter names; do not include data types, if you do you will get a duplicate

      definition error.

ð If a function returns something, declare and initialize a variable to hold the answer.

 

Function Definitions

 

ð Header (first line) must be identical to prototype except no semicolon.

ð If return type is anything other than void then you must include appropriate return statement.

ð If return type is void then you cannot include the return statement.