Menu

Function Pointers in C: Syntax, Callbacks, and qsort

Functions have addresses too, and a function pointer stores one. This page decodes the declaration syntax, shows how typedef makes it readable, and builds up to callbacks, qsort with a custom comparator, and dispatch tables.

This page includes runnable editors - edit, run, and see output instantly.

Compiled code lives in memory just like data does, so a function has an address. A function pointer is a variable that stores one - and once you can store a function, you can pass one as an argument, keep a table of them, or choose between them at runtime.

That is the capability behind sorting with a custom order, event handlers, plugin interfaces, and state machines. It is also the piece of C syntax most likely to make a reader stop and squint, so we start there.

The Declaration Syntax

Take an ordinary function:

int add(int a, int b);

To declare a pointer that can hold its address, replace the name with (*name):

int (*op)(int, int);

Read it from the inside: op is a pointer, to a function, taking (int, int), returning int.

The parentheses are not decoration. Without them, * binds to the return type instead:

int (*f)(int);   // pointer to a function taking int, returning int
int *g(int);     // FUNCTION taking int, returning int* - completely different

Here it is working end to end:

Both call forms work because a function designator converts to a pointer automatically. Write op(10, 3); the (*op)(10, 3) spelling is a holdover.

One note on that last printf: %p expects an object pointer, and function pointers are formally a separate family, so printing one is not strictly portable. The cast keeps compilers quiet on the platforms where it works at all; you will rarely need to print one.

typedef Makes It Readable

The declaration syntax gets ugly fast, and typedef is the standard remedy:

Compare int apply(BinaryOp op, int x, int y) with the raw form, int apply(int (*op)(int, int), int x, int y). Same meaning; one of them you can read at a glance. See typedef for the general rules.

Callbacks: Letting the Caller Supply Behavior

A callback is a function you hand to another function so that it can call you back at the right moment. It lets one piece of code handle the structure of a job while the caller supplies the decision.

Without function pointers you would write count_even, count_positive, and count_big - three copies of the same loop. With them, the loop is written once and the test is the parameter.

The Real Payoff: qsort

The standard library's qsort is the function-pointer example you will actually use. It sorts an array of anything, and it manages that by knowing nothing about the elements except their size and a comparator you provide.

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

The comparator receives two const void * - addresses of two elements - and returns a negative number if the first sorts before the second, zero if they tie, positive if it sorts after.

Two habits to carry from that comparator. Never write return x - y; - it looks clever and overflows for large or negative values, producing a wrong sign and a mis-sorted array. Cast before dereferencing, not after: *(const int *)a, because *a on a void * has nothing to read.

The same machinery sorts structs by any field you like:

The (q->score > p->score) - (q->score < p->score) idiom returns exactly -1, 0, or 1 with no subtraction and no overflow. And strcmp already has the right return convention, so a string comparator is a one-liner.

Tables of Function Pointers

An array of function pointers turns a chain of if/else into a lookup. This is how interpreters dispatch opcodes and how menu systems run commands.

Adding an operation is now adding a row, not editing a switch. Pair that with structs and you have the standard C plugin shape: a struct of function pointers is what "an interface" means in this language.

Things That Bite

Signatures must match exactly. A function pointer's type includes its parameter types and return type. Assigning a mismatched function is a constraint violation, and calling through a wrongly-typed pointer is undefined behavior even when it "works". Do not cast a comparator's signature to silence a warning - fix the comparator.

A null function pointer is still a null pointer. Initialize to NULL and check before calling, exactly as with data pointers:

if (handler != NULL) {
    handler(event);
}

See null pointers for why that check earns its keep.

Void pointers lose type safety, and you are the type system. Inside a qsort comparator, nothing stops you casting to the wrong type. If you sort an array of double with compare_ints, it compiles cleanly and produces nonsense.

The declarator syntax nests. A function returning a function pointer is int (*get_op(char c))(int, int);. When you find yourself writing that, reach for typedef:

typedef int (*BinaryOp)(int, int);
BinaryOp get_op(char c);           // the same thing, readable

Frequently Asked Questions

How do you declare a function pointer in C?

Write the function's signature with (*name) where the function name would go: int (*op)(int, int); declares op as a pointer to a function taking two ints and returning int. The parentheses around *op are required - without them you declare a function returning a pointer.

How do you call a function through a pointer?

Either op(3, 4) or (*op)(3, 4) - both are legal and do the same thing, because a function designator converts to a pointer automatically. Modern C code uses the plain op(3, 4) form.

What is a callback in C?

A function you hand to another function so it can call you back. qsort is the classic example: you pass a comparator, and qsort calls it whenever it needs to know which of two elements comes first. That is what lets one sort routine work on any type.

Do you need the & when taking a function's address?

No. A function name already converts to a pointer to that function, so op = add; and op = &add; are equivalent. Most C code omits the &.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED