Understanding Function Pointers in C

Published: Fri 13 April 2007
By mafr

In c/c++.

Many people familiar with object oriented programming languages like Java seem to have trouble understanding C function pointers. Even among C programmers, they are sometimes treated like an obscure feature that is avoided wherever possible. However, if you understand the concept of a pointer and know a few basic rules, they are pretty easy to use.

Most often, function pointers are used to pass user-defined behavior to library functions. The library function can then use the pointer to execute the function the pointer refers to (in object oriented languages, this is a typical use case for the strategy design pattern). A common example is the standard library's qsort(3) function:

void qsort(void *base, size_t nmemb, size_t size,
                  int (*compar)(const void *, const void *));

In its last argument, it accepts a function pointer which compares two elements in the array base. Since the array could contain virtually anything, it's up to the caller to provide a comparison function to determine the order of elements. The syntax of qsort()'s third argument looks a bit strange, but we'll get back to it later.

Let's start with a few basics first. Consider the following function:

int foo(char *str, int val) {
  printf("%s: %d\n", str, val);
  return val+1;
}

Getting a pointer to this function is easy: It is simply the name of this function, foo. Executing the function this pointer refers to is trivial, it's the standard function call syntax:

int ret;
ret = foo("Hello World", 41);

Things get more interesting if you want to store this function pointer in a variable. To declare a variable, you need its type, but what's the type of a function pointer?

Fortunately, you can derive it from the function's signature easily. Take the signature and replace the function's name with (*NAME), where NAME is the variable's name:

int (*my_fptr)(char *str, int val);

Note that this variable is only suitable for storing pointers to functions with this exact signature! It is also important to make sure the variable's name and the asterisk are enclosed by parenthesis.

You can now assign function foo to my_fptr and run the function it points too:

my_fptr = foo;
my_fptr("Test", 7);

From the assignment on, foo and my_fptr can be used interchangeably (no dereferencing necessary!), with the exception that you can assign my_fptr a different function pointer later, of course.

To simplify variable declaration, we can use a type definition. The syntax is a bit counter-intuitive, but this is how it works:

typedef int (*fptr_t)(char *str, int val);

The variable declaration looks much simpler, now that we have defined the type fptr_t:

fptr_t my_fptr;

Looking back to the qsort() example, we now understand how the last argument works. It tells us (and the compiler) that qsort() expects a pointer to a function which returns an int and expects two arguments of type const void *. The name of the argument is compar, and compar(x, y) is the syntax that is used by qsort() to call the user-provided function.

social