Behold! The function pointer:
1 2 3
At first glance the syntax appears messy. Complicated. Maybe a little dangerous. Indeed, the word “pointer” alone conjures up images of null references and segfaults and all-night debugging sessions looking for unusual bugs. Function pointers, however, are not nearly as daunting as they may appear. They’re an amazingly useful, flexible device for any programmer.
In this post, I’m going to deep-dive into C function pointers.
Let’s first discuss the C grammar for function pointers. The grammar below (slightly modified for brevity) comes from the YACC ANSI C grammar as described by Jutta Degener on her page, The YACC ANSI C grammar. I defer you to her page for grammar specifics.
declarator : pointer direct_declarator | direct_declarator ; direct_declarator : IDENTIFIER | '(' declarator ')' | direct_declarator '(' parameter_type_list ')' ... ;
The grammar for a function pointer in C is very simple. It simply takes some getting used to: Surround the ubiquitous C pointer character and an identifier in parenthesis, followed by a parameter type list also in parenthesis; the return type of the function is declared on the left-hand side of the function pointer name. For example:
This example declares
fn to be a pointer to a function which accepts two int parameters,
b. The pointed-to function returns
We can set a function pointer to the address of a function through assignment:
1 2 3 4 5
We’ve declared a function
ex and a function pointer
fn and assigned
fn the address
of the function
ex using the address-of operator, &
Function pointers may also be called through dereference, such as:
Personally, I prefer the short form:
Examine the following code which demonstrates a callback:
This is the
qsort function prototype as declared in
stdlib.h (see man 3 qsort). The
last parameter, named
compar, is a function pointer which accepts two void pointers and
Now review the following program to sort a collection of “widgets” utilizing
qsort and a
custom compare function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
The important pieces are on lines 13 through 15 and 22 through 23. Lines
13 through 15 contain the function
compare_widgets, which takes two void pointers
and returns an int. Here,
strcomp is utilized to compare the
bar member of the
qsort function on lines 22 through 23 takes as parameters the array
widgets, the size of the array and the size of the members of the array, and finally a
function pointer to a compare function, in this case
compare_widgets. As qsort iterates
over the items in the array, it calls the function pointed to by the function pointer to
determine if the array elements are less than, equal to or greater than one another.
Let’s look deeper into the implementation of C function pointers. Warning: The disassembly output below is more for guidance. Do not assume your compiler will produce the same code on your platform! Indeed, these examples are provided to demonstrate one possible implementation of function pointers at the machine level. Your mileage may vary.
All examples were built like so:
1 2 3 4
Assuming the following code revisited from above:
1 2 3 4 5
… where we once again define an example method (
ex) and assign the function pointer
to the address of
ex, we simply return the value back to the caller of
Note the optimization option, -O0 (no optimizations): In all likelihood your compiler will handle function pointers in a much more optimized fashion.
Viewing the disassembly output from GCC of the
ex method yields… unsurprising results
(for brevity, I’ve removed the assembler directives from the following examples):
1 2 3 4 5 6 7 8 9 10
Lines 2 through 4 save off the current frame pointer and set a new frame pointer while line 6 sets up the return value.
Lines 8 perform the multiplication from the parameter on the stack putting the result in EAX, the accumulator.
The interesting work is in the disassembly of main:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
The stack frame and
main parameters are established between lines 2 – 6. So we come to
line 8 which copies the address of
ex into local variable
fn (on the stack at -8(%rbp),
the first variable on the current frame).
Beginning on line 10, the address in
fn is copied to a temporary location (register RAX),
constant value 2 is copied into register EDI, and – finally – on line 12, we transfer
control to the register address copied into register RAX. This is the function pointer.
This is the magic.
1 2 3 4
Here we see the setup and call of the function. This unoptimized disassembly is not much different from an unoptimized standard function call:
Here we copy the constant value 2 into register EDI and transfer control to the
We avoid two MOV instructions under -O0 with GCC concerning the function pointer.
Just for giggles, lets see the output the same code with the optimization option of -O2:
1 2 3
Tips and Tricks
We’ve already utilized a function pointer as a parameter to a function to implement a callback. We can do so much more, however. How about an object interface? This is akin to writing object oriented code without virtual methods, though it could be easily accomplished by implementing our own vtable. I leave this as an exercise for later!
Consider the following interface:
1 2 3 4 5 6
We’re interested in the method interface: create, read, update and delete which all take a pointer to “self.” As an aside, “self” is the “this” pointer in C++, however I didn’t want to muddy the waters by introducing a C++ idiom in a discussion concerning C. Apologies to purist!
We’ve defined four methods to read a user, create a user, update a user or delete a user.
These function pointers must be defined and assigned when we instantiate a new instance (so
to speak) of our
Here’s a snippet of a fictional implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
By using a common interface such as this, the SQLite core engine can defer implementation-specific details to the file system utilized by target OS. This provides for a great deal of flexibility and improves maintainability of the core: SQLite doesn’t care how a file is written to disk, or the underlying call made to read a file. It simply utilizes this generic interface.
Function pointers are a versatile construct in C and many other languages, such as delegates in .NET. I’ve discussed their syntax, provided an example and examined the low-level implementation details of function pointers. I sincerely hope you find this post useful. Please let me know if you find any incorrect information.
Thank you for reading!
Source for this post is available on GitHub.