C Mastery / Function Pointers and Callbacks
Part 1 — The Core Language

Function Pointers and Callbacks

This chapter covers pointers to functions and their primary use case: callbacks. Function pointers are how C implements polymorphism, strategy patterns, and event-driven designs.

Why This Matters

Function pointers let you pass behavior as data. Sorting with a custom comparator, registering event handlers, and building plugin systems all rely on them. They are also the gateway to understanding how C APIs achieve extensibility.

Prerequisites

Core Concept

A function pointer stores the address of a function. Its type encodes the function's signature (parameter types and return type).

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

int (*op)(int, int);   /* op is a pointer to function (int,int)->int */
op = add;              /* function name decays to function pointer */
op = &add;             /* equivalent */
int r = op(2, 3);      /* call through the pointer */

The function name add decays to a function pointer in most contexts, and &add explicitly takes its address; both are equivalent.

Syntax

Declaration

return_type (*name)(parameter_types);

The parentheses around *name are required. Without them:

int *f(int);       /* function returning int* (NOT a function pointer) */
int (*f)(int);     /* pointer to function (int)->int */

Callback typedef

typedef int (*compare_fn)(const void *, const void *);

Calling

result = fp(args);      /* call through pointer */
result = (*fp)(args);   /* equivalent (dereference is optional) */

Examples

Sorting with a comparator (callback)

#include <stdlib.h>

int compare_int(const void *a, const void *b)
{
    const int *ia = a;
    const int *ib = b;
    return (*ia > *ib) - (*ia < *ib);
}

int main(void)
{
    int arr[] = {5, 3, 1, 4, 2};
    qsort(arr, 5, sizeof arr[0], compare_int);
    return 0;
}

qsort takes a callback (compare_int) that it calls to compare elements. The callback receives void * arguments because qsort is generic.

Callback with user data (context)

typedef void (*event_handler)(void *ctx, int event);

void notify(event_handler h, void *ctx, int event)
{
    h(ctx, event);
}

Passing ctx (often a void *) lets the callback access its own state without global variables.

Table of function pointers (dispatch table)

typedef int (*op_fn)(int, int);

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

op_fn ops[] = { add, sub };

int main(void)
{
    return ops[0](10, 5);   /* add -> 15 */
}

How It Works

A function pointer is a pointer value holding the address of the function's code. Calling it jumps to that address with the arguments per the ABI. The type encodes the signature so the compiler can check calls and generate correct argument passing.

Variations

Function pointers vs. data pointers

In ISO C, function pointers are a distinct category from object pointers. They do not portably convert to void *. Converting between function pointers and object pointers is not guaranteed. VERIFIED

Callback registries

A struct can hold a callback and its context:

struct Handler {
    void (*fn)(void *);
    void *ctx;
};

Common Mistakes

Undefined Behavior

VERIFIED

may lose information).

Portability

notable platform-specific exception).

Under the Hood

A function pointer compiles to the address of the function's entry point. A call through it is an indirect call (call *reg on x86). Indirect calls are slightly slower than direct calls due to branch-target prediction, and they interact with CFI (c.sec.cfi).

Practical Usage

Exercises

1. Declare a function pointer for a double(double) function and call it. 2. Write a map function that applies a callback to each element of an array. 3. Build a dispatch table mapping strings to functions and look up a command. 4. Demonstrate that fp(args) and (*fp)(args) both work.

Deep Challenge

Design a small plugin system using a struct that holds a name, a function pointer, and a context pointer, plus a registration function and a dispatcher. Explain the ownership and lifetime of the context pointer and how you would make the system type-safe.

References

(function calls), §6.7.6.3 (function declarators).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.func.fnptr06
c.func.callback06