Function pointers are a powerful feature in the C programming language. While they are often introduced as a way to store and call functions indirectly, their true value emerges in more advanced programming patterns. In this article, we’ll explore six advanced applications of C function pointers, complete with practical examples.
1. Callback Functions #
A callback function is registered in advance and executed when a specific event occurs. This mechanism allows you to decouple event handling logic from the main program flow.
#include <stdio.h>
void handle_event(int event_type, void (*callback)(void))
{
    printf("event %d occurred\n", event_type);
    if (callback) {
        callback();
    }
}
void callback_function()
{
    printf("callback function called\n");
}
int main()
{
    handle_event(1, callback_function); // with callback
    handle_event(2, NULL);              // no callback
    return 0;
}
Here, handle_event triggers a function dynamically through a pointer. This pattern is widely used in GUI frameworks, drivers, and signal handlers.
2. Function Parameterization #
Function pointers let you parameterize behavior dynamically, making your functions more reusable.
#include <stdio.h>
void process_array(int *array, size_t size, int (*process)(int))
{
    for(size_t i = 0; i < size; i++) {
        array[i] = process(array[i]);
    }
}
int increment(int n) { return n + 1; }
int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    size_t size = sizeof(array) / sizeof(int);
    process_array(array, size, increment);
    for (size_t i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
    return 0;
}
This allows different transformations (e.g., increment, square, negate) to be plugged in without rewriting the loop.
3. Sorting Algorithms #
By passing comparison functions, you can reuse the same sorting code for multiple orderings.
#include <stdio.h>
#include <stdlib.h>
typedef int (*compare_func_t)(const void *, const void *);
void sort(int *array, size_t size, compare_func_t compare_func)
{
    qsort(array, size, sizeof(int), compare_func);
}
int compare_int(const void *a, const void *b)
{
    return (*(int *)a - *(int *)b);
}
int compare_reverse_int(const void *a, const void *b)
{
    return (*(int *)b - *(int *)a);
}
int main()
{
    int array[] = { 3, 1, 4, 1, 5, 9 };
    size_t size = sizeof(array) / sizeof(int);
    sort(array, size, compare_int);         // ascending
    sort(array, size, compare_reverse_int); // descending
    for (size_t i = 0; i < size; i++) {
        printf("%d ", array[i]);
    }
    printf("\n");
    return 0;
}
This approach is used by the C standard library’s qsort, making your sorting functions flexible and reusable.
4. Function Pointer Arrays (Dispatch Tables) #
Arrays of function pointers can act as dispatch tables, selecting behavior at runtime.
#include <stdio.h>
void add(int a, int b)       { printf("%d + %d = %d\n", a, b, a + b); }
void subtract(int a, int b)  { printf("%d - %d = %d\n", a, b, a - b); }
void multiply(int a, int b)  { printf("%d * %d = %d\n", a, b, a * b); }
void divide(int a, int b)    { if (b) printf("%d / %d = %d\n", a, b, a / b); else printf("cannot divide by zero\n"); }
typedef void (*operation_func_t)(int, int);
int main()
{
    operation_func_t operations[] = { add, subtract, multiply, divide };
    int a = 10, b = 5;
    for (size_t i = 0; i < 4; i++) {
        operations[i](a, b);
    }
    return 0;
}
This is often used in interpreters, finite-state machines, and command dispatch systems.
5. Backtracking with Function Pointers #
Function pointers can act as custom callbacks in recursive algorithms such as backtracking.
#include <stdio.h>
typedef void (*callback_func_t)(const int *, size_t);
void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }
void permute(int *nums, size_t len, size_t depth, callback_func_t callback)
{
    if (depth == len) {
        callback(nums, len);
        return;
    }
    for (size_t i = depth; i < len; i++) {
        swap(&nums[depth], &nums[i]);
        permute(nums, len, depth + 1, callback);
        swap(&nums[depth], &nums[i]);
    }
}
void print_array(const int *arr, size_t len)
{
    for (size_t i = 0; i < len; i++) printf("%d ", arr[i]);
    printf("\n");
}
int main()
{
    int nums[] = { 1, 2, 3 };
    permute(nums, 3, 0, print_array);
    return 0;
}
By plugging in different callbacks, the permutation algorithm could count, filter, or store solutions instead of just printing them.
6. Implementing Polymorphism in C #
Although C is not object-oriented, you can simulate polymorphism using structs and function pointers.
#include <stdio.h>
typedef struct shape {
    void (*draw)(struct shape *);
} shape_t;
typedef struct circle {
    shape_t shape;
    int x, y, r;
} circle_t;
typedef struct rectangle {
    shape_t shape;
    int x, y, w, h;
} rectangle_t;
void circle_draw(shape_t *shape) {
    circle_t *c = (circle_t *)shape;
    printf("Drawing a circle at (%d, %d) with radius %d.\n", c->x, c->y, c->r);
}
void rectangle_draw(shape_t *shape) {
    rectangle_t *r = (rectangle_t *)shape;
    printf("Drawing a rectangle at (%d, %d) with width %d and height %d.\n", r->x, r->y, r->w, r->h);
}
int main()
{
    circle_t circle = { .shape = { circle_draw }, .x = 10, .y = 20, .r = 5 };
    rectangle_t rect = { .shape = { rectangle_draw }, .x = 30, .y = 40, .w = 15, .h = 20 };
    shape_t *shapes[] = { (shape_t *)&circle, (shape_t *)&rect };
    for (size_t i = 0; i < 2; i++) {
        shapes[i]->draw(shapes[i]);
    }
    return 0;
}
This technique is heavily used in C-based GUI toolkits, game engines, and embedded systems frameworks.
Conclusion #
C function pointers unlock a wide range of advanced programming techniques:
- Callbacks for event-driven systems
- Behavior parameterization for reusable functions
- Sorting flexibility via custom comparators
- Dispatch tables for interpreters or state machines
- Recursive backtracking with callback hooks
- Polymorphism in non-OOP environments
Mastering these patterns helps you write more flexible, reusable, and extensible C code, making function pointers a cornerstone of advanced C programming.
