Function Pointers and Callbacks
Passing "which function to call" as a value.
8 minute read
Big idea
Functions live in memory too, so they have addresses. A function pointer stores one — letting you pass "which function to call" around like any other value.
The idea
int add(int a, int b) { return a + b; }
int (*op)(int, int) = add; // op points to add
printf("%d\n", op(2, 3)); // calls add: 5
Read int (*op)(int, int) from the name outwards:
(*op)— op is a pointer…(int, int)— …to a function taking two ints…int— …that returns an int.
The brackets around *op matter. Without them, int *op(int, int) declares a function that returns an int *.
A function's name on its own (no ( )) already means its address, so op = add; needs no &.
#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 main(void) {
int (*op)(int, int);
op = add;
printf("%d\n", op(6, 3));
op = sub;
printf("%d\n", op(6, 3));
op = mul;
printf("%d\n", op(6, 3));
return 0;
}output
9
3
18Run and edit it in the app.
Watch it
Name the type with typedef
The syntax is noisy, so give the type a name:
typedef int (*BinaryOp)(int, int);
BinaryOp op = add;
Callbacks: passing a function in
The real power: a function can take another function as a parameter and call it back. The caller decides what happens; the function decides when.
#include <stdio.h>
typedef int (*Transform)(int);
int twice(int x) { return 2 * x; }
int square(int x) { return x * x; }
void apply(int *a, int n, Transform f) {
for (int i = 0; i < n; i++) {
a[i] = f(a[i]);
}
}
void print_all(const int *a, int n) {
for (int i = 0; i < n; i++) printf("%d ", a[i]);
printf("\n");
}
int main(void) {
int nums[] = {1, 2, 3, 4};
apply(nums, 4, twice);
print_all(nums, 4);
apply(nums, 4, square);
print_all(nums, 4);
return 0;
}output
2 4 6 8
4 16 36 64 Run and edit it in the app.
qsort: the standard library's callback
qsort (from <stdlib.h>) sorts any array — ints, strings, structs — because you supply the comparison:
void qsort(void *base, size_t count, size_t size,
int (*compare)(const void *, const void *));
Your comparator gets pointers to two elements and returns:
- negative if the first should come first,
- 0 if they're equal,
- positive if the second should come first.
The pointers are const void * — "a pointer to something, type unknown" — so you convert them to the real type first:
#include <stdio.h>
#include <stdlib.h>
int ascending(const void *pa, const void *pb) {
int a = *(const int *) pa;
int b = *(const int *) pb;
return (a > b) - (a < b);
}
int descending(const void *pa, const void *pb) {
return ascending(pb, pa);
}
int main(void) {
int nums[] = {42, 7, 19, 3, 25};
int n = sizeof nums / sizeof nums[0];
qsort(nums, n, sizeof nums[0], ascending);
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
qsort(nums, n, sizeof nums[0], descending);
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
return 0;
}output
3 7 19 25 42
42 25 19 7 3 Run and edit it in the app.
(a > b) - (a < b) gives 1, 0, or -1. You'll often see return a - b; instead — shorter, but it can overflow for big numbers of opposite sign and sort wrongly.
Sorting structs
The same qsort, a different comparator:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[16];
int mark;
} Student;
int by_mark_desc(const void *pa, const void *pb) {
const Student *a = pa;
const Student *b = pb;
return (b->mark > a->mark) - (b->mark < a->mark);
}
int main(void) {
Student s[] = {{"Ravi", 72}, {"Asha", 91}, {"Meera", 85}};
qsort(s, 3, sizeof s[0], by_mark_desc);
for (int i = 0; i < 3; i++) printf("%s %d\n", s[i].name, s[i].mark);
return 0;
}output
Asha 91
Meera 85
Ravi 72Run and edit it in the app.
A table of functions
An array of function pointers turns a menu into one line of code:
#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 main(void) {
int (*ops[])(int, int) = {add, sub, mul};
int choices[] = {2, 0, 1};
for (int i = 0; i < 3; i++) {
printf("%d\n", ops[choices[i]](10, 4));
}
return 0;
}What does it print? Show the answer
40
14
6Run and edit it in the app.
A qsort comparator returns a negative number. What does that tell qsort?
Common mistake
Calling the function when you meant to pass it: qsort(nums, n, sizeof nums[0], ascending()); calls ascending with no arguments. Pass the name — ascending — and let qsort do the calling.
Think of it like
Leaving a phone number with a receptionist: "When the parcel arrives, call this number." You don't say what they'll do in between — you just tell them who to ring. That number is a callback.
Key terms
- Function pointer — A variable holding a function's address:
int (*f)(int). - Callback — A function passed to another function, to be called by it.
- Comparator — A callback that says how two items should be ordered.
void *— A pointer to data of unknown type; convert it before use.
Check yourself
What does int (*f)(int); declare?
What should a qsort comparator return when the first element should come first?
How do you pass a comparator named cmp to qsort?