Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Pointers to Functions

Last updated Jan 1, 2003.

If you’re looking for more up-to-date information on this topic, please visit our C/C++ Programming article, podcast, and store pages.

Pointers to Functions

While many programming languges support the concept of pointers to data, only a few enable you to define pointers to code -- that is, pointers that point to functions.

Originally introduced in C, pointers to functions are widely used in C++, although pointers to member functions provide similar functionality in the case of class objects. Pointers to functions ("function pointers" for short) are useful in implementing callbacks, dynamic binding, and event-based applications.

Unfortunately, their cumbersome syntax baffles both novices and experienced programmers. In this section, I will focus on traditional C function pointers and explain how to declare and use them effectively. This will serve as the basis for pointers to members, which I will discuss in a different section.

Declaring a Function Pointer

A callback function is one that is not invoked explicitly by the programmer. Rather, the responsibility for its invocation is delegated to another function that receives its address.

To implement a callback, you first need to define an appropriate function pointer. The syntax may seem a bit arcane, but if you're familiar with function declarations in general, you may realize that a function pointer declaration is similar to a function declaration. Consider the following function prototype:

void f();

It declares a function called f() that takes no arguments and returns void. A pointer to such a function has the following type:

void (*) (); //type of f()

Let's parse it. The asterisk in the leftmost pair of parentheses is the nucleus of a function pointer declaration. Two additional elements in a function pointer declaration are the return type (void in this example), and a parameter list enclosed in the rightmost pair of parentheses (empty in this example). Note that this declaration didn't create a pointer variable yet -- it only declared the type of such a variable. So, what is it good for? You can use this type in a typedef declaration, in a sizeof expression, or as a function parameter:

// get the size of a function pointer
unsigned psize = sizeof (void (*) ()); 
// declare a typedef for a function pointer
typedef void (*pfv) ();
// used as a function parameter
void signal( void (*)() );

pfv is a synonym for "a pointer to a function that takes no arguments and returns void." You can use this typedef name to hide the cumbersome syntax of function pointers.

A pointer variable, of course, has a name. Here is an example:

void (*p) (); // p is a pointer to a function 

The name of a pointer variable appears right before the nucleus, enclosed in parentheses. You can now bind a function to p by assigning it the name of a function that has a matching signature (parameter list) and return type. For example:

void func() 
{
 /* do something */
} 
p=func; 

You may bind a different value to p so long as it's an address of a function with the same signature and return type. However, a function's name is not a part of its type.

Passing a Callback Function's Address to Its Caller

Once you've assigned a value to p you can execute the callee (the bound function) through it. To call a function through a pointer, simply treat it as if it were the function itself:

p(); //call the function bound to p

You can also pass p to another function which will invoke the callee without knowing its name:

void caller(void(*p)())
{
 p(); // call the bound function
}
void func();
int main()
{
 p = func; 
 caller(p); // pass address of func to caller
}

Remember that the assignment to p may take place at runtime, which enables you to implement dynamic binding.

Calling Conventions

So far, I've discussed function pointers without discussing compiler-specific conventions that aren't defined by the ISO standard. Many compilers have several calling conventions. For example, in Visual C++ you can precede __cdecl, __stdcall or __pascal to a function's prototype to indicate its calling convention. Borland's C++ Builder also supports the __fastcall calling convention.

A calling convention affects the compiler-generated name of a given function (i.e., its mangled name), the direction of arguments (right to left or left to right), stack cleanup responsibility (by the caller or the callee), and the mechanism for argument passing (stack, CPU registers, etc.). A calling convention is an integral part of a function's type; therefore, you can't assign an address of a function to a pointer with an incompatible calling convention. For example:

// callee is a function that takes int and returns int
__stdcall int signal(int); 

// caller is a function that takes a function pointer
void caller( __cdecl int(*ptr)(int)); 

// illegal attempt to store the address of callee in p
__cdecl int(*p)(int) = signal; // error

p and signal() have incompatible types because they have different calling conventions, although both have the same signature and return type.

Function Pointers and Ordinary Pointers

While function pointers share many similarities with ordinary data pointers, they differ in several ways. First, you can't declare a generic pointer to function similar to void*. In addition, trying to store a function pointer in a void* isn't guaranteed to work (certain compilers tolerate this, others don't). Finally, you can't dereference a function pointer -- there's no such thing as passing a function by value.

Summary

Function pointers are a powerful feature. You'll find them in the heart of almost every large-scale application. Familiarity with this feature is therefore essential for professional programmers.

To disguise the cumbersome syntax, you can use typedef names. As a C++ programmer you're probably wondering why you should bother about this C feature when C++ has better alternatives in the form of pointers to member functions and function objects. There are at least two compelling reasons for using them in C++: code that uses C functions (say, the <ctime> and <csignal> libraries which have no object-oriented alternative in C++) and static member functions. Unlike pointers to nonstatic member functions, pointers to static member functions are C function pointers, not pointers to member functions.