24.1 Declaring, Assigning, and Using Function Pointers

Just as for data pointers, we can think of three steps involved in using function pointers. First, we must declare a variable which can hold a pointer to a function, and this ends up being a somewhat complex declaration. A simple function pointer declaration looks like this:

	int (*pfi)();
This declares pfi as a pointer to a function which will return an int. As in other declarations, the * indicates that a pointer is involved, and the parentheses () indicate that a function is involved. But what about the extra parentheses around (*pfi)? They're needed because there are precedence relationships in declarations just as there are in expressions, and when the default precedence doesn't give you what you want, you have to override it with explicit parentheses. In declarations, the () indicating functions and the [] indicating arrays ``bind'' more tightly than the *'s indicating pointers. Without the extra parentheses, the declaration above would look like
	int *pfi();		/* WRONG, for pointer-to-function */
and this would declare a function returning a pointer to int. With the explicit parentheses, however, int (*pfi)() tells us that pfi is a pointer first, and that what it's a pointer to is a function, and what that function returns is an int.

It's common to use typedefs (see section 18.1.6) with complicated types such as function pointers. For example, after defining

	typedef int (*funcptr)();
the identifier funcptr is now a synonym for the type ``pointer to function returning int''. This typedef would make declaring pointers such as pfi considerably easier:
	funcptr pfi;
(In section 18.1.6, we mentioned that typedefs were a little bit like preprocessor define directives, but better. Here we see another reason: there's no way we could define a preprocessor macro which would expand to a correct function pointer declaration, but the funcptr type we just defined using typedef will work just fine.)

Once declared, a function pointer can of course be set to point to some function. If we declare some functions:

	extern int f1();
	extern int f2();
	extern int f3();
then we can set our pointer pfi to point to one of them:
	pfi = &f1;
or to one or another of them depending on some condition:
	if(condition)
		pfi = &f2;
	else	pfi = &f3;
(Of course, we're not restricted to these two forms; we can assign function pointers under any circumstances we wish. The second example could be rendered more compactly using the conditional operator: pfi = condition ? &f2 : &f3 .)

In these examples, we've used the & operator as we always have, to generate a pointer. However, when generating pointers to functions, the & is optional, because when you mention the name of a function but are not calling it, there's nothing else you could possibly be trying to do except generate a pointer to it. So, most programmers write

	pfi = f1;
or
	if(condition)
		pfi = f2;
	else	pfi = f3;
(or, equivalently, using the conditional operator, pfi = condition ? f2 : f3 ).

(The fact that a function pointer is generated automatically when a function appears in an expression but is not being called is very similar to, and in fact related to, the fact that a pointer to the first element of an array is generated automatically when an array appears in an expression.)

Finally, once we have a function pointer variable which does point to a function, we can call the function that it points to. Broken down to a near-microscopic level, this, too, is a three-step procedure. First, we write the name of the function pointer variable:

	  pfi
This is a pointer to a function. Then, we put the * operator in front, to ``take the contents of the pointer'':
	 *pfi
Now we have a function. Finally, we append an argument list in parentheses, along with an extra set of parentheses to get the precedence right, and we have a function call:
	(*pfi)(arg1, arg2)
The extra parentheses are needed here for almost exactly the same reason as they were in the declaration of pfi. Without them, we'd have
	*pfi(arg1, arg2)	/* WRONG, for pointer-to-function */
and this would say, ``call the function pfi (which had better return a pointer), passing it the arguments arg1 and arg2, and take the contents of the pointer it returns.'' However, what we want to do is take the contents of pfi (which is a pointer to a function) and call the pointed-to function, passing it the arguments arg1 and arg2. Again, the explicit parentheses override the default precedence, arranging that we apply the * operator to pfi and then do the function call.

Just to confuse things, though, parts of the syntax are optional here as well. There's nothing you can do with a function pointer except assign it to another function pointer, compare it to another function pointer, or call the function that it points to. If you write

	pfi(arg1, arg2)
it's obvious, based on the parenthesized argument list, that you're trying to call a function, so the compiler goes ahead and calls the pointed to function, just as if you'd written
	(*pfi)(arg1, arg2)
When calling the function pointed to by a function pointer, the * operator (and hence the extra set of parentheses) is optional. I prefer to use the explicit *, because that's the way I learned it and it makes a bit more sense to me that way, but you'll also see code which leaves it out.


Read sequentially: prev next up top

This page by Steve Summit // Copyright 1996-1999 // mail feedback