Motivating Example

Code reuse

Sometimes there is a particular chunk of code that appears over and over again in a program. For example: There are plenty of nice functions in standard libraries, like sqrt and cos that do all sorts of nice things for us. If only there were a function getposint that would get a positive int from the user and return it to our program, we could rewrite our program as:

int main()
{
  int a = getposint(), b = getposint();
  
  // Compute gcd
  while(b != 0)
  {
    int r = a % b;
    a = b;
    b = r;
  }

  // Write out gcd
  cout << "The gcd is " << a << endl;

  return 0;
}

This is a tremendous improvement! Unfortunately, such a function is not a part of any of the standard libraries. Therefore, it's up to us to make it!

Prototypes

You should have noticed when looking at documentation for the cmath library that they give a description of the function like:

double cos(double x);
This is called a prototype. It tells you (and it tells the compiler):

//  double           cos      ( double  x );
// \______/         \___/       \________/
// return type  function name  input parameter
Note:You need to understand prototypes to understand how expressions that involve function calls are evaluated. For example:
cos(45)/2
What happens here?
  1. Since cos takes a double object as input, the integer 45 is converted to double 45.0.
  2. Evaluating cos results in a double object as specified by the prototype.
  3. Since cos returns a double object, the integer 2 is promoted to a double 2.0 before division.
  4. We get double division and a double result.

Prototype of a user defined function

When you define functions of your own, you need to define a prototype as well. In particular,

Now, in the getposint function we'd envisioned earlier, there's nothing that the function takes as input from the program, and it should evaluate to or return the positive integer it's read in from the user, so the right prototype would be:

int getposint();

Function Definitions

In addition to giving the function prototype, you have to provide a function definition.

A function definition tells the computer what the function is supposed to do.

The function's definition can appear anywhere after the prototype outside the main block.

  1. You repeat the prototype (without the ';').
  2. Then, give a block of code (i.e., enclosed by { and } ) that comprises the function.
So our getposint function will have the following definition:

int getposint()
{
  int k;
  cout << "Enter a positive integer: ";
  cin >> k;
  while(k <= 0)
  {
    cout << "I said *positive*, try again: ";
    cin >> k;
  }

  return k;
}
The function definition also has to appear outside of the main block. This program gives a complete picture of how to rewrite our GCD calculator to make use of a getposint function.

Scope and Functions

There's some room for confusion with functions when the same name pops up in different places. For example, consider this program:

int f();

int main()
{
  int a, b;
  a = 0;
  b = f();
  cout << "a = " << a << endl;

  return 0;
}

int f()
{
  int a = 2;
  return -1;
}
What gets printed out? On the one hand, I'd say "0", since a just got assigned that value. On the other hand, the function f is called in between, and there I see a being given the value 2. So which is it? The answer is that "0" gets printed out.

It all goes back to scope:

Local variables

Since they are in different scopes, however, there is no confusion or conflict. We say that variables like this are local to the functions in which they are defined, i.e. they don't exist outside of the functions in which they are defined.

The way that you want to think of this is that each function call is like a piece of paper with boxes for each of the function's local variable.

  • When a function is called a new piece of paper is stacked on the others.
  • The computer only actually works on the function call represented by the top paper on the stack.
This animated image helps you think about how variable scope works with function calls. In the case of calling the function f() in evaluating b = f(), you should be thinking of this ...
  

Functions with an argument

More interesting are functions which take an argument, i.e. some kind of input object. For example, we may often be in the situation of having to compute something like the factorial of a number. Especially if this crops up more than once in a program, it'd be really nice to have a function like the pow or sqrt functions from cmath to do the factorial for us.

Function prototype

First, we need a prototype that specifies that our function (we'll call it factorial) takes an integer value and returns an integer value:
int factorial(int);

Function definition and argument

From this point on in our program we can use the factorial function. However, somewhere along the way we'll actually have to define the function as follows:

int factorial(int x)
{
  int f = 1;
  while (x > 0)
  {
    f = f*x;
    x--;
  }
  return f;
}

Notice that here we give a name to the int value that gets passed into the function, so that we can reference it within the body of the function definition.

Values that get passed in to a function in C++ are called function arguments. In the above, x is the argument of factorial.

Pass by value

It's important to note that the arguments are passed by value, meaning that you get a copy of the value of the variable function is called with, not the variable itself. So, for example, if our main function looked like

int main()
{
  int y = 4;
  int z = factorial(y); 
  cout << y << endl;
  return 0;
}
What will be the result?
Answer (drag your mouse): 4  
Note the following: To reiterate, x and y are different objects, and only the argument x in factorial is modified. Therefore, the variable y stays the same.

Remember: Pass by value means that a copy of the object appearing in the function call is what gets passed along to the function. In the above, y's value (i.e. 4) got passed to factorial, not the variable y itself.

Haircut Analogy Consider calling the "haircut function" with argument "MIDN Jones". Then:
  1. A clone will be made of MIDN Jones.
  2. The clone's hair will get cut
  3. The clone to get destroyed after the haircut.
  4. When MIDN Jones showed up in class the next day, his hair would still be shaggy.

Functions that return nothing

Sometimes we're interested in functions that return nothing at all. These functions are called for their side effects. This is an issue for our prototype, however, since we need to specify something for return type. In this case, we use void in place of a return type. Here's a stupid example:

void printgreetings();

int main()
{
  printgreetings();

  return 0;
}

void printgreetings()
{
  cout << "G R E E T I N G S !" << endl << endl;

  cout << "This is a little demo program." << endl
  		 << "I hope you enjoy it." << endl;   
}
This function is only used for the side effect of writing something on the screen, not for any value it would return.

Vocabulary

Problems

  1. Marathon Times
  2. Date Calculator
  3. Approximating e
  4. Adjective Endings for Numbers
  5. Writing zip codes in words