A function pointer stores the address of a function. You can call the function through the pointer, pass it to other functions as a callback, or store it in a struct to get something resembling object methods. The syntax is awkward, but the concept is straightforward once you understand how pointers work in C.
Basic Syntax
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int main() {
/* Declare a pointer to a function taking two ints, returning int */
int (*op)(int, int);
op = add; /* assign the address of add */
printf("%dn", op(10, 3)); /* call through the pointer: 13 */
op = sub;
printf("%dn", op(10, 3)); /* 7 */
return 0;
}
Read int (*op)(int, int) as: op is a pointer (*op) to a function that takes two int parameters and returns an int.
The parentheses around *op are required. Without them, int *op(int, int) declares a function named op that returns int* — not a pointer to a function.
typedef Makes the Syntax Readable
typedef int (*BinaryOp)(int, int);
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }
int apply(BinaryOp op, int a, int b) {
return op(a, b);
}
int main() {
printf("%dn", apply(add, 5, 3)); /* 8 */
printf("%dn", apply(mul, 5, 3)); /* 15 */
return 0;
}
Callbacks — Passing Functions to Functions
The C standard library uses function pointers for qsort and bsearch. You provide a comparison function:
#include <stdio.h>
#include <stdlib.h>
int compare_asc(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int compare_desc(const void *a, const void *b) {
return (*(int*)b - *(int*)a);
}
int main() {
int arr[] = {5, 2, 8, 1, 9, 3};
int n = 6;
qsort(arr, n, sizeof(int), compare_asc);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n"); /* 1 2 3 5 8 9 */
qsort(arr, n, sizeof(int), compare_desc);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
printf("n"); /* 9 8 5 3 2 1 */
return 0;
}
Dispatch Tables — Switch Without switch
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int divide(int a, int b) { return b ? a / b : 0; }
typedef int (*Op)(int, int);
int main() {
Op ops[] = { add, sub, mul, divide };
char *names[] = { "add", "sub", "mul", "div" };
int a = 20, b = 4;
for (int i = 0; i < 4; i++) {
printf("%s(%d, %d) = %dn", names[i], a, b, ops[i](a, b));
}
return 0;
}
An array of function pointers lets you select behaviour with an index — cleaner than a large switch when the operations share the same signature.
Function Pointers in Structs — Simulating Methods
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[32];
int score;
void (*print)(const void *self);
} Student;
void student_print(const void *self) {
const Student *s = (const Student*)self;
printf("%-15s %dn", s->name, s->score);
}
Student *student_new(const char *name, int score) {
Student *s = malloc(sizeof(Student));
strncpy(s->name, name, 31);
s->score = score;
s->print = student_print;
return s;
}
int main() {
Student *s = student_new("Alice", 94);
s->print(s); /* Alice 94 */
free(s);
return 0;
}
This is how C-based object systems (like GTK’s GObject and the Linux kernel’s file_operations struct) implement polymorphism — structs containing function pointers that serve as virtual method tables.
Common Mistakes
/* 1. Calling a NULL function pointer — segfault */
int (*op)(int, int) = NULL;
op(5, 3); /* crash */
/* Fix: always check before calling */
if (op != NULL) op(5, 3);
/* 2. Wrong signature — undefined behaviour */
int func(int a) { return a; }
int (*ptr)(int, int) = func; /* signature mismatch */
ptr(5, 3); /* undefined — stack likely corrupted */
Calling through a NULL function pointer produces a dangling pointer-style crash — the call jumps to address 0, which is not mapped. Always initialise function pointers and check them before calling if they might be unset.
TL;DR
- Syntax:
return_type (*name)(param_types) - Use
typedefto make the syntax readable - Use for callbacks, dispatch tables, and polymorphic structs
- Check for NULL before calling a function pointer that might be unset
- Signature must match exactly — wrong signatures cause undefined behaviour
- The C standard library uses function pointers in
qsort,bsearch,signal