UP ONE LEVEL: ENEL 315 Home Page

Pointers to Functions in C and C++
ENEL 315 Class Handout, Winter 1996

Author: Steve Norman
Date of first publication: Wednesday, March 6
Last modified: Mon Mar 4 16:00:11 MST 1996

Contents


Introduction

Pointers to functions are avaiable and useful in both C and C++.

The purpose of this handout is to explain the underlying concepts and language syntax related to pointers to functions. There isn't any material here to explain why you might want to use pointers to functions. Lab[7] has two programming exercises designed to help show how pointers to functions can be useful.

[back to top of document]


Functions have addresses

By now you should be very comfortable with the idea that every data object in a running C or C++ program has an address.

However, you might not be aware that every function has an address as well. This simple concept is essential in understanding how function calls and returns work in machine language. If you have experience working with assembly language, you may understand the idea already; if not, you will learn about it next year in ENEL 415.

When a program is running, machine code translations of all of its functions are stored in memory. Machine code consist of a sequence of instructions. An instruction is a bit pattern that tells the central processing unit (CPU) of a computer to perform a single, relatively simple action, such as copying an int from a memory location into a register within the CPU, or adding a value in one CPU register to the value in another CPU register.

A key question is, ``When the CPU finishes executing an instruction, how does it know which instruction to execute next?'' For now, you can assume that this happens by magic, but you will see in ENEL 415 the mechanisms for finding the next instruction are quite straightforward.

The address of a function is the location in computer memory where the first machine code instruction of the function is stored. Given the address, argument types, and return type of a function, it's possible to call a function using its address rather than its name. It is this fact about computer organization that allows the use of pointers to functions in languages like C and C++.

[back to top of document]


Example machine code memory layout

A typical process (a process is a running program) on a typical Unix system has its memory organized something like this:

The region of memory labeled ``machine code for functions'' is traditionally known as the text segment; this name is confusing because in this context the word text means ``machine code'', not ``characters encoded in a character set such as ASCII''.

For programs running on other systems, such as MS-DOS, some of the details may differ, but the general ideas are the same.

Now consider a simple example C program:

#include <stdio.h>

int cube(int);

int main(void)
{
  int x = 4, y;
  y = cube(x);
  printf("The cube of %d is %d.\n", x, y);
  return 0;
}

int cube(int arg)
{
  return arg * arg * arg;
}

By playing around with gdb I was able to get the information needed to draw this partial memory map of the program's machine code when the program was compiled and linked on a Sun workstation:

Note that the memory used to store a function's machine code is NOT the same as the memory used for that function's activation record(s).

To an applications programmer, the most relevant point about this example is that a function like cube really does have a well-defined address. But before moving on to pointers to functions, there are a couple of other interesting points to be made:

[back to top of document]


Example use of pointers to functions

Consider the following program:
#include <stdio.h>

void foo(int arg);
void bar(int arg);

typedef void FuncType(int);

int main(void)
{
  FuncType *func_ptr;
  func_ptr = &foo;
  (*func_ptr)(17);
  func_ptr = &bar;
  (*func_ptr)(42);
  return 0;
}

void foo(arg) { printf("foo got an arg of %d\n", arg); }

void bar(arg) { printf("bar got an arg of %d\n", arg); }
The output is
foo got an arg of 17
bar got an arg of 42
How does the program work? The first thing is to understand the declarations of FuncType and func_ptr. The typedef declaration makes FuncType an alias for the type ``function with one int argument and no return value''. That means that the type of func_ptr is ``pointer to function with one int argument and no return value''.

Next, you have to know that

    func_ptr = &foo;
means ``put the address of foo into func_ptr''.

Finally,

    (*func_ptr)(17);
means ``call the function pointed to by func_ptr with an argument of 17''.

So the two lines

    func_ptr = &foo;
    (*func_ptr)(17);
are really just a rather bizarre way of writing
    foo(17);
You'll see some much more practical uses of pointers to functions in Lab[7].

The C/C++ operator precedence rules force you to put parentheses around *func_ptr in the function call. If you wrote

    *func_ptr(17);  /* INCORRECT CODE */
the compiler would apply operators as if you had written
    *(func_ptr(17));  /* EQUIVALENT OF ABOVE INCORRECT CODE */
which is not what you want.

[back to top of document]


Function types are distinct

It's important to realize that ``pointer to function'' is not a type but a category of types. Consider this example code:
typedef void   TypeOne   (int, int);
typedef double TypeTwo   (int, int);
typedef double TypeThree (double);
typedef void   TypeFour  (int, int);
The types TypeOne, TypeTwo and TypeThree are distinct function types, because of differences in argument types or return types. So TypeOne*, TypeTwo* and TypeThree* are distinct pointer types.

On the other hand, TypeOne and TypeFour are two aliases for the same function type (function with two int arguments and no return value). So TypeOne* and TypeFour* are the same pointer type.

[back to top of document]


Nasty Syntax Issues

Some of the syntax related to pointers to functions can result in code that is difficult to read. Two major issues are:
  • pointer-to-function declarations that don't use typedef;
  • the sometimes optional nature of the * and & operators.
  • The purpose of this section is to alert you to the existence of these problems, not to explain all of the syntax rules related to pointers to functions. For the details, see an authoritative text, such as The C Programming Language, by Kernighan and Ritchie, or The C++ Programming Language, by Stroustrup.

    It's possible to declare a pointer to a function without first setting up a typedef for the function type. Here is an example of code that does this:

    void (*signal(int sig, void (*func)(int)))(int);
    
    The above line is actually a prototype for signal, which is a function belonging to the standard C library. It can be made considerably less intimidating by means of a typedef:
    typedef void SignalHandler(int);
    SignalHandler *signal(int sig, SignalHandler *func);
    
    Now it's easy to see that signal is a function that takes an int and a pointer to a SignalHandler as arguments, and returns a pointer to a SignalHandler. When you write code involving pointers to functions, use typedef to keep the code readable.

    In code that uses pointers to functions, the operators & and * are sometimes optional - you can leave them out without changing the meaning of code. This is a bit bizarre; after all, given the declarations

        int i;
        int *p;
    
    the expressions i and &i have quite different meanings, as do p and *p. The rules for pointers to functions are different. Consider this code, taken from the example program:
        func_ptr = &foo;
        (*func_ptr)(17);
    
    You could also write it this way:
        func_ptr = foo;
        (*func_ptr)(17);
    
    Or you could write it this way:
        func_ptr = &foo;
        func_ptr(17);
    
    Yet another option is:
        func_ptr = foo;
        func_ptr(17);
    
    My advice on this issue is:

    [back to top of document]