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

(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.