Compiled code lives in memory just like data does, so a function has an address too, and C lets that address be stored in a variable whose type matches the function's exact signature. Calling through a function pointer looks almost identical to calling the function directly, but which actual function runs can be decided at runtime — assigned conditionally, passed as a parameter, or stored in a data structure. This is the core mechanism behind callbacks (like qsort's comparison function) and is C's closest equivalent to first-class functions.
Declaring and calling through a function pointer
The declaration syntax is famously awkward — int (*fp)(int, int) reads as "fp is a pointer to a function taking two ints and returning int," and the parentheses around *fp are required, since int *fp(int, int) would instead declare a function returning int*.
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int (*operation)(int, int); // a pointer to a function taking (int, int), returning int
operation = add;
printf("%d\n", operation(3, 4)); // 7 — called through the pointer
operation = subtract; // reassign to a DIFFERENT function
printf("%d\n", operation(3, 4)); // -1Passing a function pointer as a callback
qsort has no idea how to compare your specific data — it calls YOUR comparison function, through the pointer you pass it, whenever it needs to compare two elements during the sort.
int compareInts(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}
int numbers[] = { 5, 2, 8, 1, 9 };
qsort(numbers, 5, sizeof(int), compareInts); // compareInts is passed AS a function pointertypedef: making the syntax bearable
A typedef for a function pointer type turns the awkward declaration syntax into a readable, reusable name — genuinely common practice whenever function pointers appear more than once in real code.
typedef int (*BinaryOp)(int, int); // BinaryOp is now a readable name for this pointer type
BinaryOp op = add;
printf("%d\n", op(3, 4)); // 7