Skip to main content

6 Advanced Applications of C Function Pointers

·931 words·5 mins
C Function Pointer Programming Advanced C
Table of Contents

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.

Related

Advanced Uses of the volatile Keyword in C
·437 words·3 mins
C Volatile
Debugging Memory Overwrite Issues in Embedded C
·601 words·3 mins
C Embedded Memory Debugging
C File I/O Tutorial with Examples: fopen, fclose, fread, fwrite
·664 words·4 mins
C File Linux