It might be handy to use a typedef
instead of declaring the function pointer each time by hand.
The syntax for declaring a typedef
for a function pointer is:
typedef returnType (*name)(parameters);
Posit that we have a function, sort
, that expects a function pointer to a function compare
such that:
compare - A compare function for two elements which is to be supplied to a sort function.
"compare" is expected to return 0 if the two elements are deemed equal, a positive value if the first element passed is "larger" in some sense than the latter element and otherwise the function returns a negative value (meaning that the first element is "lesser" than the latter).
Without a typedef
we would pass a function pointer as an argument to a function in the following manner:
void sort(int (*compare)(const void *elem1, const void *elem2)) {
/* inside of this block, the function is named "compare" */
}
With a typedef
, we'd write:
typedef int (*compare_func)(const void *, const void *);
and then we could change the function signature of sort
to:
void sort(compare_func func) {
/* In this block the function is named "func" */
}
both definitions of sort
would accept any function of the form
int compare(const void *arg1, const void *arg2) {
/* Note that the variable names do not have to be "elem1" and "elem2" */
}
Function pointers are the only place where you should include the pointer property of the type, e.g. do not try to define types like typedef struct something_struct *something_type
. This applies even for a structure with members which are not supposed to accessed directly by API callers, for example the stdio.h FILE
type (which as you now will notice is not a pointer).
A function pointer should almost always take a user-supplied void * as a context pointer.
/* function minimiser, details unimportant */
double findminimum( double (*fptr)(double x, double y, void *ctx), void *ctx)
{
...
/* repeatedly make calls like this */
temp = (*fptr)(testx, testy, ctx);
}
/* the function we are minimising, sums two cubics */
double *cubics(double x, double y, void *ctx)
{
double *coeffsx = ctx;
double *coeffsy = coeffx + 4;
return coeffsx[0] * x * x * x + coeffsx[1] * x * x + coeffsx[2] * x + coeffsx[3] +
coeffsy[0] * y * y * y + coeffsy[1] * y * y + coeffsy[2] * y + coeffsy[3];
}
void caller()
{
/* context, the coefficients of the cubics */
double coeffs[8] = {1, 2, 3, 4, 5, 6, 7, 8};
double min;
min = findminimum(cubics, coeffs);
}
Using the context pointer means that the extra parameters do not need to be hard-coded into the function pointed to, or require the use globals.
The library function qsort()
does not follow this rule, and one can often get away without context for trivial comparison functions. But for anything more complicated, the context pointer becomes essential.