Function Pointers — Syntax, Callbacks & Dispatch Tables
A function pointer stores the address of a function. Functions in C live in the text segment and can be addressed like any other data. Function pointers enable: **callbacks** (passing behaviour to generic code), **dispatch tables** (O(1) function selection instead of if/else chains), and **polymorphism** (simulating virtual functions in plain C).
Function Pointers — Declaration, Callbacks & Dispatch Table
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ── Declaration syntax ───────────────────────────────────
int (*ptr)(double, double)
↑ ↑ └──────┬──────┘
return name param types
type (in parens) */
typedef int (*Comparator)(const void *, const void *);
typedef void (*Operation)(int, int);
/* ── Callback: passing a function to another function ──── */
void apply_to_pair(int a, int b, Operation op) {
op(a, b); /* call whatever function was passed */
}
void add_print(int a, int b) { printf("%d + %d = %d
", a, b, a+b); }
void mul_print(int a, int b) { printf("%d * %d = %d
", a, b, a*b); }
void max_print(int a, int b) { printf("max(%d,%d) = %d
", a, b, a>b?a:b); }
/* ── qsort with comparator ───────────────────────────────
qsort(base, count, size, comparator)
comparator(a, b): <0 if a before b, 0 if equal, >0 if b before a */
int cmp_int_asc(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib); /* branchless: avoids subtraction overflow */
}
int cmp_str(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
/* ── Dispatch table — O(1) command dispatch ─────────────── */
typedef struct {
const char *name;
void (*fn)(int, int);
} Command;
static const Command dispatchTable[] = {
{ "add", add_print },
{ "mul", mul_print },
{ "max", max_print },
};
static const int TABLE_SIZE = (int)(sizeof(dispatchTable)/sizeof(dispatchTable[0]));
void dispatch(const char *name, int a, int b) {
for (int i = 0; i < TABLE_SIZE; i++) {
if (strcmp(dispatchTable[i].name, name) == 0) {
dispatchTable[i].fn(a, b);
return;
}
}
fprintf(stderr, "Unknown command: %s
", name);
}
int main(void) {
/* ── Callbacks ─────────────────────────────────────── */
apply_to_pair(4, 6, add_print); /* 4 + 6 = 10 */
apply_to_pair(4, 6, mul_print); /* 4 * 6 = 24 */
apply_to_pair(4, 6, max_print); /* max(4,6) = 6 */
/* ── qsort integers ─────────────────────────────────── */
int arr[] = {42, 7, -3, 19, 0, 15};
int n = (int)(sizeof(arr)/sizeof(arr[0]));
qsort(arr, n, sizeof(int), cmp_int_asc);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("
"); /* -3 0 7 15 19 42 */
/* ── qsort strings ──────────────────────────────────── */
const char *words[] = {"banana","apple","cherry","date"};
int m = 4;
qsort(words, m, sizeof(char *), cmp_str);
for (int i = 0; i < m; i++) printf("%s ", words[i]);
printf("
");
/* ── Dispatch table ─────────────────────────────────── */
dispatch("add", 3, 4);
dispatch("mul", 3, 4);
dispatch("max", 3, 4);
dispatch("div", 3, 4); /* Unknown command: div */
/* ── Function pointer variable ─────────────────────── */
Operation fn = add_print;
fn(10, 20); /* same as add_print(10,20) */
fn = mul_print;
fn(10, 20); /* now calls mul_print */
return 0;
}Common Mistakes
- Using `(ia - ib)` as a comparator — `cmp_int = { return *(int*)a - *(int*)b; }` — overflows when `a = INT_MIN` and `b = 1`. Use `(ia > ib) - (ia < ib)` for safe, branchless comparison.
- Incorrect cast of the void* in qsort comparator — for an `int[]`, cast to `const int *`: `*(const int *)a`. For a `char*[]` (array of strings), cast to `const char **`: `*(const char **)a`. Getting this wrong causes invalid memory reads.
- Not initialising a function pointer before calling it — `Operation fn; fn(1,2);` — `fn` is uninitialised garbage. Calling it is undefined behaviour that typically crashes. Always initialise function pointers.
Tip
Tip
Practice Function Pointers Syntax Callbacks Dispatch Tables in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Understanding memory layout is essential for efficient C programming
Practice Task
Note
Practice Task — (1) Write a working example of Function Pointers Syntax Callbacks Dispatch Tables from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Function Pointers Syntax Callbacks Dispatch Tables is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready c code.
Key Takeaways
- A function pointer stores the address of a function.
- Using `(ia - ib)` as a comparator — `cmp_int = { return *(int*)a - *(int*)b; }` — overflows when `a = INT_MIN` and `b = 1`. Use `(ia > ib) - (ia < ib)` for safe, branchless comparison.
- Incorrect cast of the void* in qsort comparator — for an `int[]`, cast to `const int *`: `*(const int *)a`. For a `char*[]` (array of strings), cast to `const char **`: `*(const char **)a`. Getting this wrong causes invalid memory reads.
- Not initialising a function pointer before calling it — `Operation fn; fn(1,2);` — `fn` is uninitialised garbage. Calling it is undefined behaviour that typically crashes. Always initialise function pointers.