Introduction to functions in C++

Functions are the parts of the program that do things. Generally speaking it is very useful to divide a program into a lot of small parts, each part being a function.

We have already seen one function in C++. The main() in each of our programs is a function. Some functions such as getline(cin,stringvar) are predefined (in one of the #included packages, in this case in <iostream>) and we can use such functions in most parts of a program.

Of course, C++ allows us to define our own functions. There are many reasons for doing this.

Advice: always split up a program into a number of small functions, to aid readability and to make your program easy to understand and modify.

This is an introductory page on functions. For some more details see the later page as well.

1. Defining functions

The syntax for functions in C++ is fixed by historical precident and is not ideal. You must learn it carefully.

All functions have a name, have inputs and an output. The first thing you need to define to the compiler is the number and type of the individual inputs and the type of the returned value.

Example.

In

double myfunction(int a, double b, bool c) { ... }

the function is called "myfunction". There are three inputs (also called "arguments"). The first is an int, the second is a double and the third is a bool. The value returned is a double. Of course, you will need to be able to access the input values somehow. The variable names a,b,c tell you the local names for these values within the definition of this function.

There are some special cases. You may not want any inputs at all, in which case

double myfunction1() { ... }

is fine. You may not want to return any value, in which case you must use the special "void" return type,

void myfunction2(int a, double b, bool c) { ... }

Example.

You have already seen

int main() { ... }

This is a function with no inputs that returns an int value.

Once a function, such as myfunction above, is declared then it can be used in expressions. The syntax is obvious. For example you could do,

double x = myfunction(4, 45.6, true);
double y = myfunction1();

A function that does not return a value, i.e. has return type void, sits on a line by itself.

myfunction2(4, 45.6, true);

The most important things to remember are,

It is possible to write return <value>; almost anywhere inside the { ... } body of your function, to deliberately stop any further execution of the function. If you want the same effect for a function that returns void, then just use return; on its own, with no value.

NOTE: when you submit a file with functions for assessment you must define your function in exactly the same way as indicated in the assessment details. Of course, if you give your function a different name it will be different and ignored. But equally, if you give it a different number of inputs or inputs of different type, or return value of different type then it will be regarded by C++ as a different function.

2. Examples

Example.

A simple example of a function that doesn't do anything particularly useful, but at least shows the way types and arguments are used and how you use the function is here.

// function-ex1.cpp (click here to download)

#include <iostream>

using namespace std;

/* 
   computes the double value
     a * b + 2 (when c==true)
     a * b - 1 (when c==false)
*/
double myfunction(int a, double b, bool c) {
  double x = a * b;
  if (c) {
    x = x + 2;
  } else {
    x = x - 1;
  }
  return x; // a non-void function must "return" its value
}

/* main is also a function */
int main() {

  double myval1 = myfunction( 2, 2.0, true );    // 2 * 2.0 + 2 = 6.0
  double myval2 = myfunction( 4, 3.0, false );   // 4 * 3.0 - 1 = 11.0

  // defined functions can take part in expressions as usual
  double x = 2.0;
  x = 3.0 + myfunction( 5, x, true ); // x = 3.0 + ( 5 * 2.0 + 2 ) or 15.0

  cout << "myval1 == " << myval1 << endl;
  cout << "myval2 == " << myval2 << endl;
  cout << "x == " << x << endl;

  return 0; // returns 0 from the main() function
}

Example.

Here is a similar example program that shows how you use functions returning no values (i.e. "void").

// function-ex2.cpp (click here to download)

#include <iostream>

using namespace std;

/* 
   when c = false
   prints b, b+1, b+2, ... b+|a| 

   when c = true
   prints b, b-1, b-2, ... b-|a| 
*/
void myfunction(int a, double b, bool c) {

  // convert a to its absolute value:
  if (a < 0) { a = -a; }
  // (note: function inputs can be changed like this unless they are
  // declared "const"; moreover this has no effect on the calling function,
  // unless the input is declared with "&". See later.)

  if (c) {
    for (int i=0; i<a; i++ ) {
      cout << (b + i) << " ";
    }
  } else {
    for (int i=0; i>-a; i-- ) {
      cout << (b + i) << " ";
    }
  }
  cout << endl;

  return; // a void function may use "return", but it isn't really needed here
}

int main() {

  double x = 2.0;
  int n = -4;

  // void functions are used like this
  myfunction(n,x,true);
  myfunction(n,x,false);

  // check that x,n are unchanged
  cout << "n == " << n << endl;
  cout << "x == " << x << endl;

  return 0;

}

Note that variables declared in the function declaration line are just like normal variables, and you can change their values if you like. Sometimes it is helpful to tell the compiler that you "promise not to change a variable" in which case you can mark it as const. This is a slightly advanced feature that you will occasionally have to use, but when you do need it you will be told to use it and exactly how it should be used, so you need not worry about this now.

Example.

For example,

void myfunction(const int a) {
  if (a < 0) { a = -a; }
}

generates an error message because you are not supposed to change a but the compiler thinks it may be possible to do so in a = -a; .

3. What happens inside the computer

The process of how the computer actually calculates the value of a function and then incorporates it into its calculation of some expression is interesting and useful to know about. This is not the place for the full details (but they are not so complicated) so we will look at

double myfunction(int a, double b, bool c) { ... }

as an example. Suppose an expression

2.0 + myfunction(3,x+1.0,true)

is being evaluated. Then:

The two important things to note are that (a) the computer maps out a new piece of memory, thus keeping variables distinct; and (b) the inputs to functions are done in exactly the same way as the initialisation operator = works, and (in C++) works by copying data into the input variables.

This has the consequence that, without some special trick, a function can never change the value of a variable in the place where that function was called.

4. A first look at references

We look at the way C++ can be used to change a variable from inside a called function.

Remark.

The older language C did this using something called pointers. Pointers are also in C++ and have their uses, but are error prone, very difficult for beginners, and not recommended for the kind of programming we will be doing in this module.

C++ has a feature called references which is safe and easy to use. You put an ampersand (&) in the list of inputs to a function to say that you want to refer to the original variable, and not to a copy in the function, for example because you want to change the original variable.

Example.

Consider the following program.

// badswap.cpp (click here to download)

#include <iostream>

using namespace std;

/* illustrates a problem with call-by-value */

/* try to swap the values a,b */
void badswap(int a, int b) {
  int x;
  x = a;
  a = b;
  b = x;
  // this successfully swaps the values in a,b.  but which a,b?
}

/* try to swap the values a,b */
void goodswap(int& a, int& b) {
  int x;
  x = a;
  a = b;
  b = x;
  // this successfully swaps the values in a,b.  but which a,b?
}

int main () {
  int a;
  int b;
  a = 1;
  b = 2;
  cout << "before bad swapping a==" << a << " and b==" << b << endl;
  badswap(a,b);
  cout << "after bad swapping a==" << a << " and b==" << b << endl;
  a = 1;
  b = 2;
  cout << "before good swapping a==" << a << " and b==" << b << endl;
  goodswap(a,b);
  cout << "after good swapping a==" << a << " and b==" << b << endl;
}

The program should be sef-explanatory, especially when it is compiled and run.

The first thing to say is that this program has SIX different variables called a or b, in fact three of each. There is a in badswap, a in goodswap, and a in main. Similarly for b. I deliberately chose the same names so that you realise that these variables are all different, but these names could have been anything at all. The cout statements all print values of a,b in main. Note that main doesn't have access to the other a,bs.

The new feature here is the ampersand following int. What this does is make the variables a and b into references for int values. A reference to an int value is a piece of information that tells the computer where that int value is stored, and is not the int value itself. In this case, variables a,b in goodswap are not int values, but contain information saying where to find those int values, and (in this case) reference variables a,b in goodswap actually refer at some point to the (different) variables a,b in main.

Example.

The code

// swapnames.cpp (click here to download)

// Deliberately confusing program to swap names over
#include <iostream>

using namespace std;

/* swap the name of a,b */
void swapnames(int& b, int& a) {
  cout << "in swapnames: a==" << a << " and b==" << b << endl;
  a=-1;
  b=-2;
}

int main() {  
  int a=1;
  int b=2;
  cout << "before swapping a==" << a << " and b==" << b << endl;
  swapnames(a,b);
  cout << "after swapping a==" << a << " and b==" << b << endl;
  return 0;
}

is potentially confusing, and therefore worth looking at. There are variables a,b in both main and also in swapnames. But the a in swapnames refers to the b in main and vice versa. First try to work out what it does by hand. When you think you know the answer, try it out!