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
#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; }
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
#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; }
-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:
#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; }
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:
#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; }
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:
#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; }
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).
x^2 on [0,3]: 9.000005 sin on [0,pi]: 1.999998