Prev Next

Functions

Functions

A function takes zero or more parameters, computes, and then returns a value. A function definition looks like this.

return-type function-name(parameters)
{
   statements
}

Within the statements, you can use a return statement to cause the function to stop computing and to return a value. Here is an example, a function that returns three times its parameter.

int triple(int x)
{
  return 3*x;
}
To use function triple in an expression, just provide a value for the parameter. For example, statement
n = triple(5);
sets n = 15.

Note: In the definition of function triple, parameter x is called a formal parameter. It is just a place holder. In the expression triple(5), the number 5 is called the actual parameter. It is the value that is used in place of the formal parameter.

You can use functions in arbitrarily complicated expressions. For example, statement

m = n + triple(k + 2);
sets m = n + 3(k+2).

Another example is a function that computes the length of a null-terminated string.

int length(char* s)
{
  char* p = s;
  int n = 0;
  while(*p != 0) {
    p++;
    n++;
  }
  return n;
}
To use the length function, just give it a pointer to a null-terminated string. For example,
r = length("abcd");
sets r = 4.

Here is a function that takes two parameters, and returns the larger one. Note that the parameters are separated by a comma, and each must have a type.

int max(int x, int y)
{
  if(x > y) return x;
  else return y;
}
For example,
  r = max(5,8);
sets r = 8. We can use function max to create another function max3, which computes the largest of three values.
int max3(int x, int y, int z)
{
  return max(x, max(y, z));
}

Prototypes

Sometimes, you need to tell the compiler what types of parameters a function takes, and what type it returns, without writing the function. You can use a prototype, which is just the function heading, followed by a semicolon. A prototype for function max3 is

int max3(int x, int y, int z);
In a prototype, you may include names of parameters, as is done above, or you can omit them, as in
int max3(int, int, int);

Functions that do not return values

In C/C++, each function must have a return type indicated. However, that return type can be the reserved word void, indicating that no value is returned. For example, the system function exit takes an integer parameter, and stops the program. Its prototype is

void exit(int status);
You do not need to have a return statement in a function that returns void. If you like, you can include one, but it should just say
return;

Calling modes

Three ways to pass information to a function are call-by-value, call-by-pointer and call-by reference.

Call-by-value. Call-by-value is the default calling mode. When you pass a parameter, the value of that parameter is sent to the function. The formal parameter acts as a variable local to the function that is initialized with the value of the actual parameter. If a structure is passed by value, the contents of the structure is copied into the formal parameter.

Call-by-pointer. Call-by-pointer is a special case of call-by-value in which the value that is passed is a pointer. When you have a pointer, you can do whatever you like with the memory that the pointer points to. For example, consider function increment.

void increment(int* p)
{
  (*p)++;
}
Function increment adds one to the variable that points to. For example, in
 {int x = 0;
  increment(&x);
  ...
 }
the value of x would be 1 just after the call to increment. Notice that we must pass a pointer to function increment, so we use &x to get a pointer to variable x. If you already had a pointer, you would just pass that pointer value. For example,
 {int* p = new int;
  *p = 0;
  increment(p);
  ...
 }
Here, after the increment call, variable *p holds 1. (Variable p holds a pointer to variable *p.)

What would happen if we did this?

{int* p = 0;
 increment(p);

...

}

The line int* p = 0 sets pointer variable p to 0. So p contains a null pointer. When function increment does *p, it will cause the program to abort. (You will get a memory protection error, since memory address 0 is not accessible to you.)

Call-by-pointer is the default mode for arrays, since an array is the same as a pointer to its first member. Sometimes, you want to pass an array as a parameter, but do not want to allow the array to be changed. To do that, use a constant pointer. For example, the prototype for the function length written previously should really have been

 int length(const char* p);
since the length function does not change the array that is passed to it.

Call by Reference. When you pass a variable as a parameter, it can be awkward to deal explicitly with pointers. C++ (but not C) has a way to pass variables in a more convenient way. Use & instead of * in the type of the parameter. Then the parameter is a reference parameter, and it is thought of as a variable that is being passed. For example, function increment can be written using call-by-reference.

void increment(int& x)
{
  x++;
}
Notice that variable x is now just an integer variable, not a pointer variable. To use this new increment function, you do something like this.
 {int n = 0;
  increment(n);
  ...
 }
You do not pass &n now. You just pass n as the actual parameter. The compiler passes the variable n to function increment. Within function increment, formal parameter x just names variable n. So after the call to increment, n has value 1.

When the compiler implements call-by-reference, it really just does call-by-pointer, but it inserts the & and * operations where needed automatically.

Return modes

Return-by-value. Typically, a function returns a value. For example, the max function takes two integers and returns an integer (that is, a value).

Return-by-pointer. You can have a function return a pointer. Then, the caller gets access to the memory that the pointer points to. A pointer returned by a function should usually point into either static memory or into the heap. Returning a pointer into the stack is asking for trouble, except when carefully controlled. Here is a function that allocates an integer variable in the heap, sets it to 0, and returns a pointer to the new integer variable. It also checks that the system did not run out of memory.

 int* allocInt()
 {
  int* p = new int;
  if(NULL == p) exit(1);
  else {
    *p = 0;
    return p;
  }
}

Return-by-reference. In some circumstances, you would like a function to return a variable, so that a function call expression can be used just like a variable. In such circumstances, you use return-by-reference. Often, the reference that is returned is the same as one of the reference parameters. Here is a function that increments a variable, and returns the variable.

 int& increment(int& x)
 {
  x++;
  return x;
}
Now you can write increment(increment(x)) to add two to x, if you were so inclined.


Prev Next