Skip to content

Expert · Bits, undefined behaviour and safety · topic 45 of 60 · 30 min

Function pointers and callbacks

Explanation

A function pointer stores the address of a function, so code can choose at run time which function to call. Declare one as int (*op)(int, int), assign a function name, and call it like a function. Arrays of function pointers make dispatch tables (menus, state machines), and passing them as callbacks lets libraries like qsort call your code. typedef makes the syntax readable.

Declaring and calling

c
int add(int a, int b) { return a + b; }
int mul(int a, int b) { return a * b; }

int (*op)(int, int);       /* op is a pointer to a function taking (int, int) returning int */
op = add;                  /* a function name decays to its address (&add also works) */
printf("%d\n", op(3, 4));  /* 7 */
op = mul;
printf("%d\n", op(3, 4));  /* 12 */
Careful: The brackets matter: int (*op)(int, int) is a pointer to a function, but int *op(int, int) declares a FUNCTION that returns int *. Use a typedef to avoid the confusion: typedef int (*BinOp)(int, int); BinOp op = add;.

Dispatch tables

c
typedef double (*Calc)(double, double);
double c_add(double a, double b) { return a + b; }
double c_sub(double a, double b) { return a - b; }
double c_mul(double a, double b) { return a * b; }

Calc table[] = { c_add, c_sub, c_mul };     /* index = menu choice */
const char *names[] = { "add", "subtract", "multiply" };

int choice = 2;
if (choice >= 0 && choice < 3) printf("%s: %.1f\n", names[choice], table[choice](6, 7));   /* multiply: 42.0 */
Replaces a long switch; adding an operation means adding one entry.

Callbacks: qsort

qsort (from <stdlib.h>) sorts any array. It does not know your element type, so you pass the element size and a comparison function. qsort calls your function with pointers to two elements; it must return a negative number, zero or a positive number.

c
#include <stdlib.h>

int compare_ints(const void *a, const void *b) {
    int x = *(const int *) a;
    int y = *(const int *) b;
    return (x > y) - (x < y);        /* -1, 0 or 1, without overflow */
}

typedef struct { char name[20]; int marks; } Student;
int by_marks_desc(const void *a, const void *b) {
    const Student *s = a, *t = b;
    return (t->marks > s->marks) - (t->marks < s->marks);
}

int nums[] = {42, 7, 19, 3};
qsort(nums, 4, sizeof nums[0], compare_ints);          /* 3 7 19 42 */
qsort(students, n, sizeof students[0], by_marks_desc);  /* topper first */
Careful: Writing a comparator as return x - y; looks neat but overflows when x and y are far apart (for example INT_MAX and -1), giving the wrong sign and a wrongly sorted array. Use (x > y) - (x < y). Also make sure the comparator casts to the right element type: qsort passes pointers TO elements.

Callbacks in your own code

c
typedef void (*OnAlert)(const char *msg);

void check_temperature(double t, OnAlert alert) {
    if (t > 8.0) alert("Cold store above 8 °C");
}

void sms_alert(const char *m) { printf("SMS: %s\n", m); }
void log_alert(const char *m) { fprintf(stderr, "LOG: %s\n", m); }

check_temperature(9.5, sms_alert);   /* the caller chooses what happens */
Think of it like this: A function pointer is like a speed-dial button on a phone: the button stores a number, and pressing it calls whoever is saved there. You can reprogram the button (assign another function) without changing how you press it. qsort is a helper who sorts your files and phones your speed-dial number whenever it needs to know which of two files comes first.
Tip: Callbacks often need extra data (a threshold, a counter). C APIs usually add a void *user_data parameter that is passed through to the callback, so it can reach that data without globals.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Function pointers and callbacks · 1/6

Function pointers

In this lesson you will store functions in pointers and pass them around.

Prefer a video by a teacher? Find free videos on “c function pointers callbacks qsort comparator dispatch table” on YouTube ↗

In real life: Event handlers in firmware

A smart-lock firmware registers a handler function for each event: keypad code entered, fingerprint matched, battery low. The event loop looks up the handler in a table of function pointers and calls it, so new events need only a new function and a table entry.

c
typedef enum { EV_KEYPAD, EV_FINGERPRINT, EV_BATTERY_LOW, EV_COUNT } Event;
typedef void (*Handler)(void);

void on_keypad(void)      { /* verify code, unlock */ }
void on_fingerprint(void) { /* unlock */ }
void on_battery_low(void) { /* beep three times */ }

static const Handler handlers[EV_COUNT] = { on_keypad, on_fingerprint, on_battery_low };

void dispatch(Event e) { if (e < EV_COUNT && handlers[e]) handlers[e](); }

Practice (0/3 done)

Task 1 · Question

What does this print? int sq(int x) { return x * x; } int neg(int x) { return -x; } int (*f[])(int) = { sq, neg }; printf("%d\n", f[1](f[0](3)));

Task 2 · Code · Javascript

In JavaScript, functions are values too, so the same callback idea runs here: write sortStudents(students) that returns a NEW array sorted by marks descending, then by name A–Z, using a comparator function that returns negative, zero or positive WITHOUT subtracting marks (use comparisons, like (a > b) - (a < b) in C).

Task 3 · Practice

Write a C calculator menu using a dispatch table of function pointers (add, subtract, multiply, divide with a divide-by-zero check), selected by the user's number. Then sort an array of struct Product { char name[20]; double price; } with qsort twice: by price ascending and by name, with correct comparators.

Quick quiz

Question 1

1.What does int *f(int) declare?

Question 2

2.What must a qsort comparator return?

Question 3

3.Why avoid return a - b in comparators?

Question 4

4.What is a callback?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does int *f(int) declare?

Answer: A function returning int *

Without brackets around *f, it is a function declaration.

What must a qsort comparator return?

Answer: Negative, zero or positive

Like strcmp.

Why avoid return a - b in comparators?

Answer: It can overflow and give the wrong sign

Use (a > b) - (a < b).

What is a callback?

Answer: A function passed to other code so that code can call it later

qsort's comparator is a callback.

Which copies array a (3 ints) into array b?Related

Answer: memcpy(b, a, sizeof a);

Arrays cannot be assigned; copy the bytes or loop.

From Arrays

In an error-first callback (err, data), what should you do first?Related

Answer: Check err

If err is set, data is not valid.

From Why async, and callbacks

What is a callback?Related

Answer: A function passed to another function to be called later

Callbacks are functions given to other functions, for example to run on each item or on a click.

From Functions

Where does an array NOT decay to a pointer?Related

Answer: In sizeof a

sizeof (and &a) see the whole array.

From Pointers and arrays

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Bits, undefined behaviour and safety”. The module exam comes next.
  2. Module 9 exam: Bits, undefined behaviour and safety · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Data structures in C. 60 topics left in the course before the final exam.

← Previous: Secure and defensive C