Function Pointers

Functions have addresses too, and a function pointer stores one. Passing functions around lets you customise behaviour — the comparison used by a sort, the action for a menu item, a callback when an event happens — and is how C achieves much of what other languages do with lambdas or interfaces.

Declaring and calling

C
#include <stdio.h>

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

int main(void) {
    int (*operation)(int, int);   // pointer to a function taking (int, int) and returning int

    operation = add;              // a function's name gives its address
    printf("%d\n", operation(6, 7));

    operation = multiply;
    printf("%d\n", operation(6, 7));
    return 0;
}
Output
13
42

The parentheses around *operation matter: int *operation(int, int) would declare a function returning int *. You call through the pointer just like calling a function.

typedef makes them readable

C
#include <stdio.h>

typedef double (*Transform)(double);

double half(double x) { return x / 2; }
double square(double x) { return x * x; }
double negate(double x) { return -x; }

void apply_all(double *values, size_t n, Transform f) {
    for (size_t i = 0; i < n; i++) values[i] = f(values[i]);
}

int main(void) {
    double data[] = { 1, 4, 9 };
    apply_all(data, 3, square);
    apply_all(data, 3, half);
    apply_all(data, 3, negate);
    printf("%.1f %.1f %.1f\n", data[0], data[1], data[2]);
    return 0;
}
Output
-0.5 -8.0 -40.5

qsort: the standard example

The library's qsort can sort any array, because you give it a function that compares two elements. The comparison receives const void * pointers and returns negative, zero or positive:

C
#include <stdio.h>
#include <stdlib.h>

int by_ascending(const void *a, const void *b) {
    int x = *(const int *) a, y = *(const int *) b;
    return (x > y) - (x < y);
}

int by_descending(const void *a, const void *b) {
    return by_ascending(b, a);
}

int main(void) {
    int scores[] = { 72, 95, 64, 88, 95, 51 };
    size_t n = sizeof scores / sizeof scores[0];

    qsort(scores, n, sizeof scores[0], by_ascending);
    for (size_t i = 0; i < n; i++) printf("%d ", scores[i]);
    printf("\n");

    qsort(scores, n, sizeof scores[0], by_descending);
    for (size_t i = 0; i < n; i++) printf("%d ", scores[i]);
    printf("\n");
    return 0;
}
Output
51 64 72 88 95 95
95 95 88 72 64 51

(x > y) - (x < y) gives −1, 0 or 1. The tempting return x - y; can overflow for large values. Lesson 46 sorts structs and uses bsearch.

A dispatch table

An array of function pointers maps choices to actions without a long switch:

C
#include <stdio.h>
#include <string.h>

typedef void (*Command)(void);

void cmd_help(void)  { printf("commands: help, hello, about\n"); }
void cmd_hello(void) { printf("Hello there!\n"); }
void cmd_about(void) { printf("A tiny command shell.\n"); }

struct entry {
    const char *name;
    Command run;
};

static const struct entry commands[] = {
    { "help", cmd_help },
    { "hello", cmd_hello },
    { "about", cmd_about },
};

void dispatch(const char *input) {
    for (size_t i = 0; i < sizeof commands / sizeof commands[0]; i++) {
        if (strcmp(input, commands[i].name) == 0) {
            commands[i].run();
            return;
        }
    }
    printf("unknown command: %s\n", input);
}

int main(void) {
    const char *typed[] = { "hello", "about", "dance", "help" };
    for (int i = 0; i < 4; i++) dispatch(typed[i]);
    return 0;
}
Output
Hello there!
A tiny command shell.
unknown command: dance
commands: help, hello, about

Adding a command means adding one function and one table line. This pattern is common in interpreters, state machines and device drivers.

Callbacks with user data

C callbacks usually take an extra void * argument so the caller can pass along whatever context the callback needs:

C
#include <stdio.h>

typedef void (*Visitor)(int value, void *context);

void for_each(const int *values, size_t n, Visitor visit, void *context) {
    for (size_t i = 0; i < n; i++) visit(values[i], context);
}

void add_to_total(int value, void *context) {
    int *total = context;
    *total += value;
}

void print_if_even(int value, void *context) {
    (void) context;                   // unused
    if (value % 2 == 0) printf("%d ", value);
}

int main(void) {
    int data[] = { 3, 8, 12, 5, 6 };
    int total = 0;
    for_each(data, 5, add_to_total, &total);
    printf("total %d\n", total);
    for_each(data, 5, print_if_even, NULL);
    printf("\n");
    return 0;
}
Output
total 34
8 12 6

Try it yourself

Write a function that integrates any function numerically over an interval using the trapezoid rule, and test it on x² from 0 to 3 (exact answer: 9).

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
x^2 on [0,3]:   9.000005
sin on [0,pi]: 1.999998