Functions


This material discusses:

  1. Why functions?
  2. Parts of a function
  3. Returning a value
  4. Calling a function
  5. Call-by-value
  6. Call-by-reference
  7. Function prototype
  8. Exercise


  1. Why functions?

    Often, we need to use one set of code over and over again. Functions allow us to write code once and reuse it many times.

    For example, we might use a function to calculate the length of the hypotenuse of a right triangle:

    cout << "Enter triangle width: ";
    cin >> w;
    cout << "Enter triangle height: ";
    cin >> h;
    cout << "The hypotenuse is " << hypotenuse(w, h) << endl;
    

    Using functions can act as documentation (if they are given appropriate names). Compare the instant recognition of hypotenuse(w, h) to sqrt(w*w+h*h).

  2. Parts of a function:

    Functions in C++ follow this basic format:

    return-type functionName(parameter-list)
    {
      body
    }
    

    The return-type, function name and parameter-list form the header of the function.

    The body contains statements to be performed by the function. You may declare additional variables in the body--such variables only exist inside the function.

    Thus, a complete function might be:

    double hypotenuse(double width, double height)
    {
      double width_squared = width * width;
      double height_squared = height * height;
      return sqrt(width_squared + height_squared);
    }
    

  3. Returning a value:

    The return statement is used to return a value from a function.

    For functions (other than those that return nothing, i.e., void), the syntax is:

    return expression;
    
    The expression should either be of the same type as the function's return type or of a type the compiler can convert to its return type. For example,

    float func1()
    {
      int a;
      ...
      return a;
    }
    
    int func2()
    {
      int i;
      ...
      return i + 4.0;
    }
    
    func1() is ok (since int can be safely converted to float); however, func2() is not ok since the reverse is not true.


    Note: Functions that have a non-void return type must return a value under all circumstances.

    With a void return type, you use return by itself. If you want the function to just end after the last statement, no return is needed. For example,

    void speakIfAllowed(bool allowed)
    {
      if (!allowed)
        return;
      cout << "Hello!" << endl; // Function ends after this statement.
    }
    

    Returning from a function immediately exits the function. Thus, the output above is never seen if allowed is not true.

  4. Calling a function:

    To use a function, you must call it.

    Basically, you just give the function's name following by a (, a comma-separated list of values you are passing to the function, and a ). For example:

    hypotenuse(w, h)
    


    Note: Types are not listed for a function call.

    If the function has a return type other than void, you may use the return value (you don't have to):

    ...
    double c;
    c = hypotenuse(a, b);
    cout << "The hypotenuse of a right triangle " << endl
         << "with width=10 and height=2 is "
         << hypotenuse(10, 2) << endl;
    

  5. Call-by-value:

    Normally, when you pass parameters to a function (by calling it), the function gets copies of those values. Thus, any changes you make to parameters doesn't affect any passed variables. For example:

    int nextInt(int i)
    {
      i++;
      return i;
    }
    
    int main()
    {
      int j = 10;
      cout << "Next int is " << nextInt(j) << endl;
    }
    
    The output would be "Next int is 11", but j would still be 10.

    Whether the formal parameter (i) and the actual parameter (j) have different names or the same name--i.e., they are different variables.

    This kind of parameter passing is named "call-by-value".

  6. Call-by-reference:

    Sometimes, you really do want to change parameters. For example, here is an unsuccessful swapping function:

    void swap1(int i, int j)
    {
      int temp = i;
      i = j;
      j = temp;
    }
    
    int main()
    {
      int a = 4, b = 5;
      swap1(a, b);
      cout << "a is " << a << " and b is " << b << endl;
    }
    
    The output is "a is 4 and b is 5"!

    To be able to change parameters, you must list them by reference in the parameter list. Do so by adding ampersands (&) to each:

    void swap2(int &i, int &j)
    {
      int temp = i;
      i = j;
      j = temp;
    }
    
    The function body is the same as the unsuccessful version. The function call also looks the same:
    int main()
    {
      int a = 4, b = 5;
      swap2(a, b);
      cout << "a is " << a << " and b is " << b << endl;
    }
    
    but now we get the desired answer "a is 5 and b is 4".

    Since functions can only have one return value, call-by-reference can also be useful when you need to return more than one thing. For example,

    void getNameParts(string fullname,
                      string &firstname,
                      string &lastname)
    {
      ...
    }
    

    Finally, unlike with call-by-value, where you can pass literals as parameters:

    hypotenuse(10, 6)
    
    you really need to pass variables for parameters that are by reference:
    int a = 4, b = 5;
    swap2(a, b); // YES
    swap2(2, 4); // NO!
    

  7. Function prototype:

    So far, we have just been giving function definitions for functions. I.e.,

    double hypotenuse(double width, double height)
    {
      double width_squared = width * width;
      double height_squared = height * height;
      return sqrt(width_squared + height_squared);
    }
    
    That's fine if the function definition appears before you call the function:
    ... hypotenuse(w, h) ...
    
    However, sometimes a function may be defined after a call to it. Or, the definition may be somewhere else, like in a library. In such cases, we must provide a function prototype for the function before it is called. For example,
    // prototype
    double hypotenuse(double width, double height);
    
    ... hypotenuse(w, h) // call
    
    // definition
    double hypotenuse(double width, double height)
    {
      double width_squared = width * width;
      double height_squared = height * height;
      return sqrt(width_squared + height_squared);
    }
    

    Note that the prototype looks just like the header of the function (with a semi-colon at the end). Although the types of parameters are required, names are not. I.e., we could have written the prototype as:

    double hypotenuse(double, double);
    
    However, parameter names often add to readability and should be included in most cases.

    A prototype allows the compiler to check that the number and type of parameters match between the function and its call and to do any necessary conversion of passed parameters. For example,

    hypotenuse(w, h, d);  // Wrong, only takes 2 parameters!
    hypotenuse(4, 5);     // Convert to double, then call function.
    

    When a function definition appears before any call to that function, the definition acts like a prototype.


    Note: Header files often contain prototypes. For example, math.h has prototypes for the functions in the math library.

  8. Exercise:

    We'd like a program that makes change. In other words, given the total amount of change to be given to a customer, it will tell you how many dollars and coins to give out.

    A run of such a program looks like (things you type are bold):

    % makechange
    How much change to make (in cents): 1099
    
    Your change is:
    10 dollars
    3 quarters
    2 dimes
    0 nickels
    4 pennies
    

    We have provided a skeleton for this program, makechange.cpp. Fill in the function prototype, function definition, and function call for the function makeChange().

    When you are done, compile and run the program to test it.

    Hint: Some of the arithmetic operations (e.g., +, -, *, / (quotient) or % (modulus)) may be useful in the body of the function.


BU CAS CS - Functions
Copyright © 1993-2000 by Robert I. Pitts <rip at bu dot edu>. All Rights Reserved.