C Programming 3 min read

Function pointers and callbacks

A function pointer holds the address of a function, so code can choose at run time what to call. A typedef keeps the syntax readable. Callbacks let a driver call back into your code, dispatch tables replace long switch statements, and an interrupt vector table is the same idea in hardware.

A const array named handlers holds three function pointers. Index 0 calls on_temp, index 1 calls on_light and index 2 calls on_error. Each takes an int and returns nothing.
A dispatch table: the index picks the function, and every entry has the same signature.

Pointers to code

A function's name, used without parentheses, is its address. void (*f)(int) declares f as a pointer to a function taking an int and returning nothing; f(42) calls whatever it points at. The raw syntax is hard to read, so give the type a name: typedef void (*handler_t)(int value);.

Callbacks and dispatch tables

A callback is a function pointer passed as an argument: a driver that finishes a reading, a timer that expires or a received byte calls the function you registered, without knowing anything else about your code. A dispatch table is an array of function pointers indexed by a command, message ID or event, which replaces a long switch with one line. Declaring the table const lets the linker keep it in Flash.

callbacks.cC
#include <stdio.h>typedef void (*handler_t)(int value);static void on_temp(int v)  { printf("temp: %d C\n", v); }static void on_light(int v) { printf("light: %d lux\n", v); }static void on_error(int v) { printf("error code %d\n", v); }/* A dispatch table: an array of function pointers. */static const handler_t handlers[] = { on_temp, on_light, on_error };/* A callback: the caller says what to do with the result. */static void read_sensor(int raw, handler_t done) {    done(raw / 10);}int main(void) {    for (unsigned id = 0; id < sizeof handlers / sizeof handlers[0]; id++) {        handlers[id](42);    }    read_sensor(250, on_temp);    return 0;}
Output
temp: 42 C
light: 42 lux
error code 42
temp: 25 C

The vector table

When an interrupt fires, the processor has to find its handler. On an Arm Cortex-M, the vector table is an array of handler addresses that the hardware reads directly: entry 0 holds the initial stack pointer, entry 1 the reset handler, and the rest the exception and interrupt handlers. Startup code usually defines it in C or assembly as exactly such an array. On some other architectures the table holds jump instructions instead of addresses.