Docs

Function Pointers

Function Pointers in C

Table of Contents

  1. •Introduction
  2. •Basic Concepts
  3. •Declaring Function Pointers
  4. •Using Function Pointers
  5. •Function Pointers as Parameters
  6. •Callback Functions
  7. •Arrays of Function Pointers
  8. •Function Pointers in Structures
  9. •Typedef with Function Pointers
  10. •Generic Programming
  11. •State Machines
  12. •Plugin Architecture
  13. •Common Patterns
  14. •Pitfalls and Best Practices
  15. •Summary

Introduction

A function pointer is a pointer that points to the address of a function rather than a data value. Function pointers enable:

  • •Callback mechanisms: Pass functions as arguments to other functions
  • •Dynamic function dispatch: Select which function to call at runtime
  • •Event handling: Register handlers for various events
  • •Plugin systems: Load and execute code dynamically
  • •State machines: Implement clean state transitions
  • •Generic algorithms: Create reusable sorting, searching, etc.
  • •Object-oriented patterns: Implement virtual methods and polymorphism

Function pointers are fundamental to many C libraries and frameworks, including:

  • •qsort() and bsearch() in the standard library
  • •Signal handlers
  • •Thread creation with pthread
  • •GUI event callbacks

Basic Concepts

Functions Have Addresses

Like variables, functions are stored in memory and have addresses:

#include <stdio.h>

void greet(void) {
    printf("Hello, World!\n");
}

int main(void) {
    // Print the address of the function
    printf("Address of greet: %p\n", (void*)greet);
    printf("Address of main:  %p\n", (void*)main);

    return 0;
}

Function Name as Pointer

The function name without parentheses is a pointer to the function:

void foo(void) { }

// These are equivalent:
void (*ptr1)(void) = foo;
void (*ptr2)(void) = &foo;  // & is optional

// Calling through pointer (these are equivalent):
ptr1();
(*ptr1)();

Declaring Function Pointers

Syntax

The declaration syntax mirrors the function signature:

return_type (*pointer_name)(parameter_types);

Examples

// Pointer to function taking no args, returning void
void (*ptr1)(void);

// Pointer to function taking int, returning int
int (*ptr2)(int);

// Pointer to function taking two ints, returning int
int (*ptr3)(int, int);

// Pointer to function taking char*, returning char*
char* (*ptr4)(char*);

// Pointer to function taking void*, returning void
void (*ptr5)(void*);

// Pointer to function taking int, int*, returning double
double (*ptr6)(int, int*);

Reading Function Pointer Declarations

Use the "spiral rule" or work inside-out:

int (*fp)(int, int)
     │   └───────── parameters: (int, int)
     └───────────── pointer to function
 └────────────────── returning int

// So: fp is a pointer to a function that takes two ints and returns an int

Complex Examples

// Pointer to function returning pointer to int
int* (*fp1)(void);

// Pointer to function taking function pointer, returning function pointer
int (*(*fp2)(int (*)(int)))(int);

// Array of function pointers
void (*fp_array[10])(int);

// Pointer to array of function pointers
void (*(*fp_ptr_array)[])(int);

Using Function Pointers

Basic Assignment and Calling

#include <stdio.h>

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

int subtract(int a, int b) {
    return a - b;
}

int main(void) {
    // Declare and assign function pointer
    int (*operation)(int, int);

    // Assign to add function
    operation = add;
    printf("5 + 3 = %d\n", operation(5, 3));

    // Reassign to subtract function
    operation = subtract;
    printf("5 - 3 = %d\n", operation(5, 3));

    return 0;
}

Comparing Function Pointers

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

int main(void) {
    int (*fp)(int, int) = add;

    if (fp == add) {
        printf("fp points to add\n");
    }

    if (fp != multiply) {
        printf("fp does not point to multiply\n");
    }

    // Check for NULL
    if (fp != NULL) {
        int result = fp(2, 3);
    }

    return 0;
}

NULL Function Pointers

int (*fp)(int) = NULL;

// Always check before calling
if (fp != NULL) {
    int result = fp(42);
}

// Or use the pointer directly in condition
if (fp) {
    fp(42);
}

Function Pointers as Parameters

Basic Callback

#include <stdio.h>

// Function that accepts a callback
void process(int data, void (*callback)(int)) {
    printf("Processing data: %d\n", data);

    // Call the callback with the result
    if (callback != NULL) {
        callback(data * 2);
    }
}

// Callback function
void print_result(int result) {
    printf("Result: %d\n", result);
}

void save_result(int result) {
    printf("Saving result %d to file...\n", result);
}

int main(void) {
    process(10, print_result);  // Result: 20
    process(10, save_result);   // Saving result 20 to file...
    process(10, NULL);          // No callback

    return 0;
}

Generic Comparison Function

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

// Comparison function type
typedef int (*CompareFunc)(const void*, const void*);

// Find maximum element using comparison function
void* find_max(void *array, size_t count, size_t size, CompareFunc compare) {
    if (count == 0) return NULL;

    char *base = (char*)array;
    void *max = base;

    for (size_t i = 1; i < count; i++) {
        void *current = base + i * size;
        if (compare(current, max) > 0) {
            max = current;
        }
    }

    return max;
}

// Compare integers
int compare_int(const void *a, const void *b) {
    return (*(int*)a - *(int*)b);
}

// Compare strings
int compare_str(const void *a, const void *b) {
    return strcmp(*(char**)a, *(char**)b);
}

int main(void) {
    int numbers[] = {3, 1, 4, 1, 5, 9, 2, 6};
    int *max_num = find_max(numbers, 8, sizeof(int), compare_int);
    printf("Max number: %d\n", *max_num);

    char *names[] = {"Charlie", "Alice", "Bob", "David"};
    char **max_name = find_max(names, 4, sizeof(char*), compare_str);
    printf("Max name: %s\n", *max_name);

    return 0;
}

Callback Functions

Event-Driven Callbacks

#include <stdio.h>

// Event types
typedef enum {
    EVENT_START,
    EVENT_STOP,
    EVENT_ERROR
} EventType;

// Callback type
typedef void (*EventCallback)(EventType event, void *data);

// Event handler structure
typedef struct {
    EventCallback callback;
    void *user_data;
} EventHandler;

// Global event handlers
#define MAX_HANDLERS 10
static EventHandler handlers[MAX_HANDLERS];
static int handler_count = 0;

// Register callback
void register_handler(EventCallback callback, void *user_data) {
    if (handler_count < MAX_HANDLERS) {
        handlers[handler_count].callback = callback;
        handlers[handler_count].user_data = user_data;
        handler_count++;
    }
}

// Trigger event
void trigger_event(EventType event) {
    for (int i = 0; i < handler_count; i++) {
        if (handlers[i].callback) {
            handlers[i].callback(event, handlers[i].user_data);
        }
    }
}

// Example callback
void log_event(EventType event, void *data) {
    const char *name = (const char*)data;
    const char *event_names[] = {"START", "STOP", "ERROR"};
    printf("[%s] Event: %s\n", name, event_names[event]);
}

int main(void) {
    register_handler(log_event, "Logger1");
    register_handler(log_event, "Logger2");

    trigger_event(EVENT_START);
    trigger_event(EVENT_STOP);

    return 0;
}

Timer Callbacks

#include <stdio.h>
#include <time.h>

typedef void (*TimerCallback)(void *data);

typedef struct {
    time_t trigger_time;
    TimerCallback callback;
    void *data;
    int active;
} Timer;

// Set timer
void set_timer(Timer *timer, int seconds, TimerCallback callback, void *data) {
    timer->trigger_time = time(NULL) + seconds;
    timer->callback = callback;
    timer->data = data;
    timer->active = 1;
}

// Check and fire timers
void check_timers(Timer *timers, int count) {
    time_t now = time(NULL);

    for (int i = 0; i < count; i++) {
        if (timers[i].active && now >= timers[i].trigger_time) {
            timers[i].callback(timers[i].data);
            timers[i].active = 0;
        }
    }
}

void on_timer(void *data) {
    printf("Timer fired: %s\n", (char*)data);
}

int main(void) {
    Timer timers[3];

    set_timer(&timers[0], 1, on_timer, "Timer 1");
    set_timer(&timers[1], 2, on_timer, "Timer 2");
    set_timer(&timers[2], 3, on_timer, "Timer 3");

    // Simple event loop
    while (timers[0].active || timers[1].active || timers[2].active) {
        check_timers(timers, 3);
    }

    return 0;
}

Arrays of Function Pointers

Basic Array

#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 divide(int a, int b) { return b != 0 ? a / b : 0; }

int main(void) {
    // Array of function pointers
    int (*operations[4])(int, int) = {add, subtract, multiply, divide};
    char *names[] = {"add", "subtract", "multiply", "divide"};

    int a = 20, b = 5;

    for (int i = 0; i < 4; i++) {
        printf("%d %s %d = %d\n", a, names[i], b, operations[i](a, b));
    }

    return 0;
}

Menu-Driven Program

#include <stdio.h>

void option_new(void)    { printf("Creating new file...\n"); }
void option_open(void)   { printf("Opening file...\n"); }
void option_save(void)   { printf("Saving file...\n"); }
void option_exit(void)   { printf("Goodbye!\n"); }

typedef void (*MenuOption)(void);

typedef struct {
    const char *name;
    MenuOption action;
} MenuItem;

int main(void) {
    MenuItem menu[] = {
        {"New",  option_new},
        {"Open", option_open},
        {"Save", option_save},
        {"Exit", option_exit}
    };
    int menu_size = sizeof(menu) / sizeof(menu[0]);

    int choice;
    do {
        printf("\n=== Menu ===\n");
        for (int i = 0; i < menu_size; i++) {
            printf("%d. %s\n", i + 1, menu[i].name);
        }
        printf("Choice: ");
        scanf("%d", &choice);

        if (choice >= 1 && choice <= menu_size) {
            menu[choice - 1].action();
        }
    } while (choice != menu_size);

    return 0;
}

Jump Table (Dispatch Table)

#include <stdio.h>

// Calculator operations
typedef enum {
    OP_ADD = '+',
    OP_SUB = '-',
    OP_MUL = '*',
    OP_DIV = '/'
} Operation;

double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div_op(double a, double b) { return b != 0 ? a / b : 0; }

typedef double (*BinaryOp)(double, double);

// Create dispatch table
BinaryOp get_operation(char op) {
    static BinaryOp table[256] = {NULL};
    static int initialized = 0;

    if (!initialized) {
        table['+'] = add;
        table['-'] = sub;
        table['*'] = mul;
        table['/'] = div_op;
        initialized = 1;
    }

    return table[(unsigned char)op];
}

double calculate(double a, char op, double b) {
    BinaryOp operation = get_operation(op);
    if (operation) {
        return operation(a, b);
    }
    printf("Unknown operation: %c\n", op);
    return 0;
}

int main(void) {
    printf("10 + 5 = %.2f\n", calculate(10, '+', 5));
    printf("10 - 5 = %.2f\n", calculate(10, '-', 5));
    printf("10 * 5 = %.2f\n", calculate(10, '*', 5));
    printf("10 / 5 = %.2f\n", calculate(10, '/', 5));

    return 0;
}

Function Pointers in Structures

Basic Structure with Function Pointers

#include <stdio.h>

typedef struct {
    int x, y;
    void (*print)(struct Point *self);
    void (*move)(struct Point *self, int dx, int dy);
} Point;

// Forward declaration needed
typedef struct Point Point;

void point_print(Point *self) {
    printf("Point(%d, %d)\n", self->x, self->y);
}

void point_move(Point *self, int dx, int dy) {
    self->x += dx;
    self->y += dy;
}

Point point_create(int x, int y) {
    Point p = {
        .x = x,
        .y = y,
        .print = point_print,
        .move = point_move
    };
    return p;
}

int main(void) {
    Point p = point_create(10, 20);

    p.print(&p);       // Point(10, 20)
    p.move(&p, 5, 5);
    p.print(&p);       // Point(15, 25)

    return 0;
}

Object-Oriented Interface

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

// Shape "class" with virtual methods
typedef struct Shape {
    const char *name;
    double (*area)(struct Shape *self);
    double (*perimeter)(struct Shape *self);
    void (*draw)(struct Shape *self);
    void (*destroy)(struct Shape *self);
} Shape;

// Rectangle "subclass"
typedef struct {
    Shape base;
    double width, height;
} Rectangle;

double rectangle_area(Shape *self) {
    Rectangle *r = (Rectangle*)self;
    return r->width * r->height;
}

double rectangle_perimeter(Shape *self) {
    Rectangle *r = (Rectangle*)self;
    return 2 * (r->width + r->height);
}

void rectangle_draw(Shape *self) {
    Rectangle *r = (Rectangle*)self;
    printf("Drawing rectangle: %.1f x %.1f\n", r->width, r->height);
}

void rectangle_destroy(Shape *self) {
    free(self);
}

Shape* rectangle_create(double width, double height) {
    Rectangle *r = malloc(sizeof(Rectangle));
    r->base.name = "Rectangle";
    r->base.area = rectangle_area;
    r->base.perimeter = rectangle_perimeter;
    r->base.draw = rectangle_draw;
    r->base.destroy = rectangle_destroy;
    r->width = width;
    r->height = height;
    return (Shape*)r;
}

// Circle "subclass"
typedef struct {
    Shape base;
    double radius;
} Circle;

double circle_area(Shape *self) {
    Circle *c = (Circle*)self;
    return 3.14159 * c->radius * c->radius;
}

double circle_perimeter(Shape *self) {
    Circle *c = (Circle*)self;
    return 2 * 3.14159 * c->radius;
}

void circle_draw(Shape *self) {
    Circle *c = (Circle*)self;
    printf("Drawing circle: radius %.1f\n", c->radius);
}

Shape* circle_create(double radius) {
    Circle *c = malloc(sizeof(Circle));
    c->base.name = "Circle";
    c->base.area = circle_area;
    c->base.perimeter = circle_perimeter;
    c->base.draw = circle_draw;
    c->base.destroy = rectangle_destroy;  // Same as rectangle
    c->radius = radius;
    return (Shape*)c;
}

// Polymorphic function - works with any shape!
void print_shape_info(Shape *shape) {
    printf("%s:\n", shape->name);
    printf("  Area: %.2f\n", shape->area(shape));
    printf("  Perimeter: %.2f\n", shape->perimeter(shape));
    shape->draw(shape);
}

int main(void) {
    Shape *shapes[] = {
        rectangle_create(5, 3),
        circle_create(4),
        rectangle_create(10, 2)
    };
    int count = 3;

    for (int i = 0; i < count; i++) {
        print_shape_info(shapes[i]);
        printf("\n");
    }

    // Cleanup
    for (int i = 0; i < count; i++) {
        shapes[i]->destroy(shapes[i]);
    }

    return 0;
}

Typedef with Function Pointers

Simplifying Function Pointer Types

// Without typedef (hard to read)
void (*callback)(int, void*);
void register_callback(void (*callback)(int, void*), void *data);

// With typedef (much cleaner)
typedef void (*Callback)(int, void*);
Callback callback;
void register_callback(Callback callback, void *data);

Complete Example

#include <stdio.h>

// Define function pointer types
typedef int (*BinaryIntOp)(int, int);
typedef void (*VoidCallback)(void);
typedef int (*Comparator)(const void*, const void*);
typedef void (*EventHandler)(int event_id, void *data);

// Operations
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; }

// Use the typedef
BinaryIntOp operations[] = {add, subtract, multiply};

int apply_operation(BinaryIntOp op, int a, int b) {
    return op ? op(a, b) : 0;
}

int main(void) {
    for (int i = 0; i < 3; i++) {
        printf("Result: %d\n", apply_operation(operations[i], 10, 5));
    }

    return 0;
}

Function Pointer Returning Function Pointer

#include <stdio.h>

typedef int (*Operation)(int, int);

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 divide(int a, int b) { return b ? a / b : 0; }

// Function that returns a function pointer
// Without typedef: int (*get_operation(char op))(int, int)
// With typedef:
Operation get_operation(char op) {
    switch (op) {
        case '+': return add;
        case '-': return subtract;
        case '*': return multiply;
        case '/': return divide;
        default:  return NULL;
    }
}

int main(void) {
    char operators[] = {'+', '-', '*', '/'};

    for (int i = 0; i < 4; i++) {
        Operation op = get_operation(operators[i]);
        if (op) {
            printf("20 %c 5 = %d\n", operators[i], op(20, 5));
        }
    }

    return 0;
}

Generic Programming

Generic Sorting

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

typedef int (*CompareFunc)(const void*, const void*);

// Generic bubble sort
void generic_sort(void *base, size_t count, size_t size, CompareFunc compare) {
    char *arr = (char*)base;
    char *temp = malloc(size);

    for (size_t i = 0; i < count - 1; i++) {
        for (size_t j = 0; j < count - i - 1; j++) {
            void *a = arr + j * size;
            void *b = arr + (j + 1) * size;

            if (compare(a, b) > 0) {
                // Swap
                memcpy(temp, a, size);
                memcpy(a, b, size);
                memcpy(b, temp, size);
            }
        }
    }

    free(temp);
}

// Compare functions
int compare_int(const void *a, const void *b) {
    return *(int*)a - *(int*)b;
}

int compare_int_desc(const void *a, const void *b) {
    return *(int*)b - *(int*)a;
}

int compare_string(const void *a, const void *b) {
    return strcmp(*(char**)a, *(char**)b);
}

int main(void) {
    // Sort integers ascending
    int numbers[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(numbers) / sizeof(numbers[0]);

    generic_sort(numbers, n, sizeof(int), compare_int);

    printf("Sorted ascending: ");
    for (int i = 0; i < n; i++) printf("%d ", numbers[i]);
    printf("\n");

    // Sort integers descending
    generic_sort(numbers, n, sizeof(int), compare_int_desc);

    printf("Sorted descending: ");
    for (int i = 0; i < n; i++) printf("%d ", numbers[i]);
    printf("\n");

    // Sort strings
    char *names[] = {"Charlie", "Alice", "Eve", "Bob", "David"};
    int name_count = sizeof(names) / sizeof(names[0]);

    generic_sort(names, name_count, sizeof(char*), compare_string);

    printf("Sorted names: ");
    for (int i = 0; i < name_count; i++) printf("%s ", names[i]);
    printf("\n");

    return 0;
}

Generic Map/Filter/Reduce

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

typedef void (*MapFunc)(void *element);
typedef int (*FilterFunc)(const void *element);
typedef void (*ReduceFunc)(void *accumulator, const void *element);

// Generic map: apply function to each element
void generic_map(void *array, size_t count, size_t size, MapFunc func) {
    char *ptr = (char*)array;
    for (size_t i = 0; i < count; i++) {
        func(ptr + i * size);
    }
}

// Generic filter: create new array with matching elements
void* generic_filter(void *array, size_t count, size_t size,
                     FilterFunc predicate, size_t *result_count) {
    // First pass: count matching elements
    char *ptr = (char*)array;
    size_t match_count = 0;

    for (size_t i = 0; i < count; i++) {
        if (predicate(ptr + i * size)) {
            match_count++;
        }
    }

    // Allocate result array
    void *result = malloc(match_count * size);
    char *dest = (char*)result;

    // Second pass: copy matching elements
    for (size_t i = 0; i < count; i++) {
        void *element = ptr + i * size;
        if (predicate(element)) {
            memcpy(dest, element, size);
            dest += size;
        }
    }

    *result_count = match_count;
    return result;
}

// Generic reduce: fold array into single value
void generic_reduce(void *array, size_t count, size_t size,
                    void *accumulator, ReduceFunc func) {
    char *ptr = (char*)array;
    for (size_t i = 0; i < count; i++) {
        func(accumulator, ptr + i * size);
    }
}

// Example functions
void double_int(void *element) {
    *(int*)element *= 2;
}

int is_even(const void *element) {
    return (*(int*)element % 2) == 0;
}

void sum_int(void *acc, const void *element) {
    *(int*)acc += *(int*)element;
}

int main(void) {
    int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    size_t count = sizeof(numbers) / sizeof(numbers[0]);

    // Map: double each element
    printf("Original: ");
    for (size_t i = 0; i < count; i++) printf("%d ", numbers[i]);
    printf("\n");

    generic_map(numbers, count, sizeof(int), double_int);

    printf("Doubled:  ");
    for (size_t i = 0; i < count; i++) printf("%d ", numbers[i]);
    printf("\n");

    // Filter: keep even numbers
    size_t filtered_count;
    int *evens = generic_filter(numbers, count, sizeof(int), is_even, &filtered_count);

    printf("Even only: ");
    for (size_t i = 0; i < filtered_count; i++) printf("%d ", evens[i]);
    printf("\n");

    // Reduce: sum all
    int sum = 0;
    generic_reduce(numbers, count, sizeof(int), &sum, sum_int);
    printf("Sum: %d\n", sum);

    free(evens);

    return 0;
}

State Machines

Basic State Machine

#include <stdio.h>

// States
typedef enum {
    STATE_IDLE,
    STATE_RUNNING,
    STATE_PAUSED,
    STATE_STOPPED,
    STATE_COUNT
} State;

// Events
typedef enum {
    EVENT_START,
    EVENT_PAUSE,
    EVENT_RESUME,
    EVENT_STOP,
    EVENT_COUNT
} Event;

// State handler type
typedef State (*StateHandler)(Event event);

// State handlers
State state_idle(Event event) {
    printf("[IDLE] ");
    switch (event) {
        case EVENT_START:
            printf("Starting...\n");
            return STATE_RUNNING;
        default:
            printf("Ignoring event in IDLE\n");
            return STATE_IDLE;
    }
}

State state_running(Event event) {
    printf("[RUNNING] ");
    switch (event) {
        case EVENT_PAUSE:
            printf("Pausing...\n");
            return STATE_PAUSED;
        case EVENT_STOP:
            printf("Stopping...\n");
            return STATE_STOPPED;
        default:
            printf("Continuing to run...\n");
            return STATE_RUNNING;
    }
}

State state_paused(Event event) {
    printf("[PAUSED] ");
    switch (event) {
        case EVENT_RESUME:
            printf("Resuming...\n");
            return STATE_RUNNING;
        case EVENT_STOP:
            printf("Stopping...\n");
            return STATE_STOPPED;
        default:
            printf("Still paused...\n");
            return STATE_PAUSED;
    }
}

State state_stopped(Event event) {
    printf("[STOPPED] ");
    switch (event) {
        case EVENT_START:
            printf("Restarting...\n");
            return STATE_RUNNING;
        default:
            printf("Staying stopped...\n");
            return STATE_STOPPED;
    }
}

// State machine
typedef struct {
    State current_state;
    StateHandler handlers[STATE_COUNT];
} StateMachine;

void sm_init(StateMachine *sm) {
    sm->current_state = STATE_IDLE;
    sm->handlers[STATE_IDLE] = state_idle;
    sm->handlers[STATE_RUNNING] = state_running;
    sm->handlers[STATE_PAUSED] = state_paused;
    sm->handlers[STATE_STOPPED] = state_stopped;
}

void sm_process_event(StateMachine *sm, Event event) {
    StateHandler handler = sm->handlers[sm->current_state];
    if (handler) {
        sm->current_state = handler(event);
    }
}

int main(void) {
    StateMachine sm;
    sm_init(&sm);

    sm_process_event(&sm, EVENT_START);   // IDLE -> RUNNING
    sm_process_event(&sm, EVENT_PAUSE);   // RUNNING -> PAUSED
    sm_process_event(&sm, EVENT_START);   // PAUSED: ignore
    sm_process_event(&sm, EVENT_RESUME);  // PAUSED -> RUNNING
    sm_process_event(&sm, EVENT_STOP);    // RUNNING -> STOPPED
    sm_process_event(&sm, EVENT_START);   // STOPPED -> RUNNING

    return 0;
}

Table-Driven State Machine

#include <stdio.h>

typedef enum { STATE_A, STATE_B, STATE_C, STATE_COUNT } State;
typedef enum { EVENT_X, EVENT_Y, EVENT_Z, EVENT_COUNT } Event;

typedef void (*ActionFunc)(void);

// Action functions
void action_a_x(void) { printf("Action: A->B on X\n"); }
void action_a_y(void) { printf("Action: A stays on Y\n"); }
void action_b_x(void) { printf("Action: B->C on X\n"); }
void action_b_z(void) { printf("Action: B->A on Z\n"); }
void action_c_y(void) { printf("Action: C->A on Y\n"); }
void action_none(void) { }

// Transition table entry
typedef struct {
    State next_state;
    ActionFunc action;
} Transition;

// Transition table
static Transition transition_table[STATE_COUNT][EVENT_COUNT] = {
    // STATE_A
    {
        {STATE_B, action_a_x},  // EVENT_X
        {STATE_A, action_a_y},  // EVENT_Y
        {STATE_A, action_none}  // EVENT_Z
    },
    // STATE_B
    {
        {STATE_C, action_b_x},  // EVENT_X
        {STATE_B, action_none}, // EVENT_Y
        {STATE_A, action_b_z}   // EVENT_Z
    },
    // STATE_C
    {
        {STATE_C, action_none}, // EVENT_X
        {STATE_A, action_c_y},  // EVENT_Y
        {STATE_C, action_none}  // EVENT_Z
    }
};

State process(State current, Event event) {
    Transition t = transition_table[current][event];
    t.action();
    return t.next_state;
}

int main(void) {
    State state = STATE_A;

    const char *state_names[] = {"A", "B", "C"};
    Event events[] = {EVENT_X, EVENT_Y, EVENT_X, EVENT_Z, EVENT_Y};
    const char *event_names[] = {"X", "Y", "X", "Z", "Y"};

    printf("Starting in state %s\n", state_names[state]);

    for (int i = 0; i < 5; i++) {
        printf("Event %s: ", event_names[i]);
        state = process(state, events[i]);
        printf("New state: %s\n", state_names[state]);
    }

    return 0;
}

Plugin Architecture

Simple Plugin System

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

#define MAX_PLUGINS 10

// Plugin interface
typedef struct {
    const char *name;
    const char *version;
    int (*init)(void);
    void (*shutdown)(void);
    int (*process)(const char *input, char *output, size_t output_size);
} Plugin;

// Plugin registry
static Plugin plugins[MAX_PLUGINS];
static int plugin_count = 0;

// Register plugin
int register_plugin(Plugin *plugin) {
    if (plugin_count >= MAX_PLUGINS) {
        return -1;
    }

    plugins[plugin_count++] = *plugin;
    printf("Registered plugin: %s v%s\n", plugin->name, plugin->version);
    return 0;
}

// Initialize all plugins
void init_all_plugins(void) {
    for (int i = 0; i < plugin_count; i++) {
        if (plugins[i].init) {
            plugins[i].init();
        }
    }
}

// Shutdown all plugins
void shutdown_all_plugins(void) {
    for (int i = plugin_count - 1; i >= 0; i--) {
        if (plugins[i].shutdown) {
            plugins[i].shutdown();
        }
    }
}

// Find and use plugin
int use_plugin(const char *name, const char *input, char *output, size_t size) {
    for (int i = 0; i < plugin_count; i++) {
        if (strcmp(plugins[i].name, name) == 0) {
            if (plugins[i].process) {
                return plugins[i].process(input, output, size);
            }
        }
    }
    return -1;
}

// Example plugins
int uppercase_init(void) {
    printf("Uppercase plugin initialized\n");
    return 0;
}

void uppercase_shutdown(void) {
    printf("Uppercase plugin shut down\n");
}

int uppercase_process(const char *input, char *output, size_t size) {
    size_t len = strlen(input);
    if (len >= size) len = size - 1;

    for (size_t i = 0; i < len; i++) {
        output[i] = toupper(input[i]);
    }
    output[len] = '\0';
    return 0;
}

int reverse_process(const char *input, char *output, size_t size) {
    size_t len = strlen(input);
    if (len >= size) len = size - 1;

    for (size_t i = 0; i < len; i++) {
        output[i] = input[len - 1 - i];
    }
    output[len] = '\0';
    return 0;
}

int main(void) {
    // Create and register plugins
    Plugin uppercase = {
        .name = "uppercase",
        .version = "1.0",
        .init = uppercase_init,
        .shutdown = uppercase_shutdown,
        .process = uppercase_process
    };

    Plugin reverse = {
        .name = "reverse",
        .version = "1.0",
        .init = NULL,
        .shutdown = NULL,
        .process = reverse_process
    };

    register_plugin(&uppercase);
    register_plugin(&reverse);

    init_all_plugins();

    // Use plugins
    char output[100];

    use_plugin("uppercase", "hello world", output, sizeof(output));
    printf("Uppercase: %s\n", output);

    use_plugin("reverse", "hello world", output, sizeof(output));
    printf("Reverse: %s\n", output);

    shutdown_all_plugins();

    return 0;
}

Common Patterns

Strategy Pattern

#include <stdio.h>

// Strategy interface
typedef void (*CompressionStrategy)(const char *data);

void compress_zip(const char *data) {
    printf("Compressing '%s' using ZIP\n", data);
}

void compress_gzip(const char *data) {
    printf("Compressing '%s' using GZIP\n", data);
}

void compress_lz4(const char *data) {
    printf("Compressing '%s' using LZ4\n", data);
}

// Context
typedef struct {
    CompressionStrategy strategy;
} Compressor;

void compressor_set_strategy(Compressor *c, CompressionStrategy strategy) {
    c->strategy = strategy;
}

void compressor_compress(Compressor *c, const char *data) {
    if (c->strategy) {
        c->strategy(data);
    }
}

int main(void) {
    Compressor compressor = {0};
    const char *data = "sample data";

    compressor_set_strategy(&compressor, compress_zip);
    compressor_compress(&compressor, data);

    compressor_set_strategy(&compressor, compress_gzip);
    compressor_compress(&compressor, data);

    compressor_set_strategy(&compressor, compress_lz4);
    compressor_compress(&compressor, data);

    return 0;
}

Observer Pattern

#include <stdio.h>

#define MAX_OBSERVERS 10

typedef void (*Observer)(int value);

typedef struct {
    int value;
    Observer observers[MAX_OBSERVERS];
    int observer_count;
} Subject;

void subject_init(Subject *s) {
    s->value = 0;
    s->observer_count = 0;
}

void subject_attach(Subject *s, Observer observer) {
    if (s->observer_count < MAX_OBSERVERS) {
        s->observers[s->observer_count++] = observer;
    }
}

void subject_notify(Subject *s) {
    for (int i = 0; i < s->observer_count; i++) {
        s->observers[i](s->value);
    }
}

void subject_set_value(Subject *s, int value) {
    s->value = value;
    subject_notify(s);
}

// Observers
void logger(int value) {
    printf("Logger: value changed to %d\n", value);
}

void alert(int value) {
    if (value > 100) {
        printf("ALERT: Value %d exceeds threshold!\n", value);
    }
}

void display(int value) {
    printf("Display: [%d]\n", value);
}

int main(void) {
    Subject subject;
    subject_init(&subject);

    subject_attach(&subject, logger);
    subject_attach(&subject, alert);
    subject_attach(&subject, display);

    subject_set_value(&subject, 50);
    printf("\n");
    subject_set_value(&subject, 150);

    return 0;
}

Pitfalls and Best Practices

Common Pitfalls

1. Calling NULL Function Pointer

// BAD: Crashes if fp is NULL
int (*fp)(int) = NULL;
int result = fp(42);  // Crash!

// GOOD: Always check
if (fp != NULL) {
    int result = fp(42);
}

2. Incorrect Function Signature

// BAD: Signature mismatch
int add(int a, int b) { return a + b; }
void (*fp)(int) = (void (*)(int))add;  // Wrong!

// GOOD: Match signatures exactly
int (*fp)(int, int) = add;  // Correct

3. Dangling Function Pointers

// In a dynamically loaded library scenario:
void *handle = dlopen("plugin.so", RTLD_NOW);
void (*func)(void) = dlsym(handle, "plugin_func");

// BAD: Using func after closing
dlclose(handle);
func();  // Undefined behavior!

// GOOD: Close only when done
// ... use func ...
dlclose(handle);

4. Forgetting const for Comparison Functions

// BAD: May cause undefined behavior with qsort
int compare(void *a, void *b) { ... }

// GOOD: Use const for qsort-style comparators
int compare(const void *a, const void *b) { ... }

Best Practices

1. Use Typedef for Clarity

// Hard to read
void register(void (*callback)(int, void*), void *data);

// Much cleaner
typedef void (*Callback)(int, void*);
void register(Callback callback, void *data);

2. Document Function Pointer Contracts

/**
 * Comparison function for sorting.
 *
 * @param a Pointer to first element
 * @param b Pointer to second element
 * @return negative if a < b, zero if a == b, positive if a > b
 */
typedef int (*CompareFunc)(const void *a, const void *b);

3. Use Default/No-op Functions

void default_callback(int event) {
    // No-op - intentionally empty
}

typedef struct {
    void (*on_event)(int);
} Handler;

// Initialize with default instead of NULL
Handler h = { .on_event = default_callback };

// Now safe to call without checking
h.on_event(42);

4. Use Const When Appropriate

// Function won't be changed
void process(const void (*handler)(int event));

// Array of function pointers that won't change
const Operation operations[] = { add, subtract, multiply };

Summary

Key Concepts

  1. •Declaration: return_type (*name)(params)
  2. •Assignment: fp = function_name (& is optional)
  3. •Calling: fp(args) or (*fp)(args)
  4. •Typedef: Simplifies complex declarations
  5. •Arrays: Implement jump tables and dispatch tables
  6. •Callbacks: Pass behavior as parameters
  7. •Structures: Implement OOP-like patterns

Common Use Cases

PatternDescription
CallbacksNotify caller of events or completion
ComparatorsCustom sorting/searching behavior
State MachinesClean state transition logic
Plugin SystemsExtensible application architecture
Strategy PatternRuntime algorithm selection
Virtual MethodsOOP polymorphism in C

Quick Reference

// Declaration
int (*fp)(int, int);

// With typedef
typedef int (*BinaryOp)(int, int);
BinaryOp fp;

// Array of function pointers
int (*ops[4])(int, int) = {add, sub, mul, div};

// Function returning function pointer
typedef int (*Op)(int, int);
Op get_operation(char symbol);

// In structure
typedef struct {
    void (*init)(void);
    void (*process)(int);
    void (*cleanup)(void);
} Interface;

Best Practices Summary

  1. •Always check for NULL before calling
  2. •Use typedef for readability
  3. •Match function signatures exactly
  4. •Document callback contracts
  5. •Consider using default/no-op functions
  6. •Use const where appropriate
  7. •Be careful with dynamic library functions

Function pointers are a powerful feature that enables flexible, extensible, and elegant C programs. Mastering them opens the door to implementing many advanced programming patterns.

Function Pointers - C Programming Tutorial | DeepML