A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
A function pointer holds the address of a function. You can store it in a variable, pass it as an argument, return it from a function, and call through it. This is how C implements callbacks, event handlers, comparators, and virtual dispatch. qsort takes a comparator function pointer. signal takes a handler function pointer. Every plugin architecture in C is built on function pointer tables. Mastering function pointers is the final step in understanding C's abstraction mechanisms.
#include <stdio.h>int add(int a, int b) { return a + b; }int subtract(int a, int b) { return a - b; }int multiply(int a, int b) { return a * b; }int main(void) {int (*op)(int, int) = add;printf("add(3, 4): %d\n", op(3, 4));op = subtract;printf("subtract(10, 3): %d\n", op(10, 3));op = multiply;printf("multiply(6, 7): %d\n", op(6, 7));return 0;}
#include <stdio.h>typedef int (*operation_fn)(int, int);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; }typedef struct {const char *name;operation_fn fn;} Command;int main(void) {Command commands[] = {{"add", add},{"sub", sub},{"mul", mul},};int n = 3;const char *user_input = "mul";int a = 8, b = 5;for (int i = 0; i < n; i++) {if (strcmp(commands[i].name, user_input) == 0) {printf("%s(%d, %d) = %d\n",commands[i].name, a, b, commands[i].fn(a, b));break;}}return 0;}
The dispatch table maps names to functions. Adding a new operation requires adding one entry to the array. No switch statement to update. No if-else chain to extend. The lookup is linear, but for small tables it is faster than a chain of string comparisons in a switch. For large tables, sort and binary search, or use a hash table. The pattern scales from three commands to three thousand.
#include <stdio.h>typedef void (*callback_fn)(int value, void *ctx);void for_each(int *arr, int n, callback_fn cb, void *ctx) {for (int i = 0; i < n; i++) {cb(arr[i], ctx);}}void print_value(int value, void *ctx) {const char *prefix = (const char *)ctx;printf("%s%d\n", prefix, value);}void sum_value(int value, void *ctx) {int *total = (int *)ctx;*total += value;}int main(void) {int data[] = {10, 20, 30, 40};int n = 4;for_each(data, n, print_value, " item: ");int total = 0;for_each(data, n, sum_value, &total);printf("Sum: %d\n", total);return 0;}
for_each is generic. It does not know what the callback does. The callback receives the element and a context pointer. The context carries any state the callback needs: a prefix string, an accumulator, a file handle. This is the C idiom for higher-order functions. It is how qsort, pthread_create, and every event loop in C work.
Function pointers are C's polymorphism mechanism. They decouple the caller from the callee. They enable callbacks, dispatch tables, plugin architectures, and strategy patterns. The syntax is ugly. The power is real. Learn to read the declarations. Use typedefs to hide them. Next: goto statements, the controversial control flow primitive that has exactly one good use case.
Finish the lesson once you have worked through the material. This awards ★ 40 XP.