Function pointers in C are far more powerful than simple callback mechanisms. Advanced usage of function pointers enables sophisticated programming paradigms: dynamic dispatch, plugin architectures, state machines, closures (simulated), virtual tables, and even basic forms of object-oriented programming. For experienced C developers, mastering these advanced techniques unlocks the ability to write highly flexible, extensible, and maintainable code.
What Makes Function Pointers Advanced?
Advanced function pointer usage goes beyond simple callbacks to include:
- Arrays of function pointers for dispatch tables
- Function pointers as structure members for polymorphic behavior
- Returning function pointers from functions (function factories)
- Pointer to function pointers (double indirection)
- Generic algorithms using function pointers
- State machines with function pointer transitions
- Closures and captured context using
void*parameters
Dispatch Tables: Replacing Switch Statements
#include <stdio.h> #include <string.h> #include <ctype.h> // ============================================================ // DISPATCH TABLES - REPLACING LONG SWITCH STATEMENTS // ============================================================ // Command handler functions typedef struct { char *command; void (*handler)(char *args); char *help; } Command; // Handler implementations void cmd_help(char *args) { printf("Available commands:\n"); // Help text would be here } void cmd_exit(char *args) { printf("Goodbye!\n"); exit(0); } void cmd_echo(char *args) { if (args) printf("%s\n", args); } void cmd_calc(char *args) { // Simple calculator parsing int a, b; char op; if (sscanf(args, "%d %c %d", &a, &op, &b) == 3) { switch(op) { case '+': printf("= %d\n", a + b); break; case '-': printf("= %d\n", a - b); break; case '*': printf("= %d\n", a * b); break; case '/': if (b != 0) printf("= %d\n", a / b); else printf("Division by zero\n"); break; default: printf("Unknown operator\n"); } } else { printf("Usage: calc <num> <op> <num>\n"); } } // Command dispatch table Command commands[] = { {"help", cmd_help, "Show this help"}, {"exit", cmd_exit, "Exit the program"}, {"echo", cmd_echo, "Echo arguments"}, {"calc", cmd_calc, "Simple calculator"}, {NULL, NULL, NULL} // Sentinel }; // Fast lookup using hash table (for larger command sets) #define HASH_SIZE 256 typedef struct CommandNode { char *command; void (*handler)(char *); struct CommandNode *next; } CommandNode; CommandNode *hashTable[HASH_SIZE]; unsigned int hash(const char *str) { unsigned int hash = 5381; int c; while ((c = *str++)) { hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash % HASH_SIZE; } void registerCommand(const char *cmd, void (*handler)(char *)) { unsigned int h = hash(cmd); CommandNode *node = malloc(sizeof(CommandNode)); node->command = strdup(cmd); node->handler = handler; node->next = hashTable[h]; hashTable[h] = node; } void (*getHandler(const char *cmd))(char *) { unsigned int h = hash(cmd); CommandNode *node = hashTable[h]; while (node) { if (strcmp(node->command, cmd) == 0) { return node->handler; } node = node->next; } return NULL; } int main() { printf("=== Dispatch Table Example ===\n"); // Linear search through command table char input[100]; char cmd[50]; char *args; while (1) { printf("\n> "); fgets(input, sizeof(input), stdin); input[strcspn(input, "\n")] = 0; // Parse command and arguments char *space = strchr(input, ' '); if (space) { *space = '\0'; strcpy(cmd, input); args = space + 1; } else { strcpy(cmd, input); args = NULL; } // Look up command in table int found = 0; for (int i = 0; commands[i].command != NULL; i++) { if (strcmp(commands[i].command, cmd) == 0) { commands[i].handler(args); found = 1; break; } } if (!found) { printf("Unknown command: %s\n", cmd); } } return 0; } Polymorphism and Virtual Tables (VTable)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> // ============================================================ // POLYMORPHISM WITH VIRTUAL TABLES (C's version of C++ vtables) // ============================================================ // Forward declarations typedef struct Shape Shape; typedef struct ShapeVTable ShapeVTable; // Virtual table structure - contains function pointers for polymorphic behavior struct ShapeVTable { double (*area)(const Shape*); double (*perimeter)(const Shape*); void (*draw)(const Shape*); void (*destroy)(Shape*); }; // Base Shape structure - must be first in all derived structs struct Shape { ShapeVTable *vtable; char name[32]; }; // Generic functions that work for any Shape double shape_area(const Shape *shape) { return shape->vtable->area(shape); } double shape_perimeter(const Shape *shape) { return shape->vtable->perimeter(shape); } void shape_draw(const Shape *shape) { shape->vtable->draw(shape); } void shape_destroy(Shape *shape) { if (shape && shape->vtable && shape->vtable->destroy) { shape->vtable->destroy(shape); } else if (shape) { free(shape); } } // ==================== CIRCLE ==================== typedef struct { Shape base; // Base struct must be first double radius; } Circle; // Circle's implementations of virtual functions double circle_area(const Shape *shape) { const Circle *circle = (const Circle*)shape; return M_PI * circle->radius * circle->radius; } double circle_perimeter(const Shape *shape) { const Circle *circle = (const Circle*)shape; return 2 * M_PI * circle->radius; } void circle_draw(const Shape *shape) { const Circle *circle = (const Circle*)shape; printf("Drawing Circle '%s' with radius %.2f\n", shape->name, circle->radius); } void circle_destroy(Shape *shape) { printf("Destroying Circle '%s'\n", shape->name); free(shape); } // Circle's virtual table ShapeVTable circle_vtable = { .area = circle_area, .perimeter = circle_perimeter, .draw = circle_draw, .destroy = circle_destroy }; // Constructor Circle* circle_create(const char *name, double radius) { Circle *circle = malloc(sizeof(Circle)); circle->base.vtable = &circle_vtable; strncpy(circle->base.name, name, 31); circle->base.name[31] = '\0'; circle->radius = radius; return circle; } // ==================== RECTANGLE ==================== typedef struct { Shape base; double width; double height; } Rectangle; // Rectangle's implementations double rectangle_area(const Shape *shape) { const Rectangle *rect = (const Rectangle*)shape; return rect->width * rect->height; } double rectangle_perimeter(const Shape *shape) { const Rectangle *rect = (const Rectangle*)shape; return 2 * (rect->width + rect->height); } void rectangle_draw(const Shape *shape) { const Rectangle *rect = (const Rectangle*)shape; printf("Drawing Rectangle '%s' (%.2f x %.2f)\n", shape->name, rect->width, rect->height); } // Rectangle's virtual table ShapeVTable rectangle_vtable = { .area = rectangle_area, .perimeter = rectangle_perimeter, .draw = rectangle_draw, .destroy = circle_destroy // Can reuse same destroy if no special needs }; Rectangle* rectangle_create(const char *name, double width, double height) { Rectangle *rect = malloc(sizeof(Rectangle)); rect->base.vtable = &rectangle_vtable; strncpy(rect->base.name, name, 31); rect->base.name[31] = '\0'; rect->width = width; rect->height = height; return rect; } // ==================== TRIANGLE ==================== typedef struct { Shape base; double a, b, c; // Three sides } Triangle; double triangle_area(const Shape *shape) { const Triangle *tri = (const Triangle*)shape; // Heron's formula double s = (tri->a + tri->b + tri->c) / 2.0; return sqrt(s * (s - tri->a) * (s - tri->b) * (s - tri->c)); } double triangle_perimeter(const Shape *shape) { const Triangle *tri = (const Triangle*)shape; return tri->a + tri->b + tri->c; } void triangle_draw(const Shape *shape) { const Triangle *tri = (const Triangle*)shape; printf("Drawing Triangle '%s' with sides %.2f, %.2f, %.2f\n", shape->name, tri->a, tri->b, tri->c); } ShapeVTable triangle_vtable = { .area = triangle_area, .perimeter = triangle_perimeter, .draw = triangle_draw, .destroy = circle_destroy }; Triangle* triangle_create(const char *name, double a, double b, double c) { // Validate triangle inequality if (a + b <= c || a + c <= b || b + c <= a) { return NULL; // Invalid triangle } Triangle *tri = malloc(sizeof(Triangle)); tri->base.vtable = &triangle_vtable; strncpy(tri->base.name, name, 31); tri->base.name[31] = '\0'; tri->a = a; tri->b = b; tri->c = c; return tri; } int main() { printf("=== Polymorphism with Virtual Tables ===\n\n"); // Create an array of different shapes Shape *shapes[5]; shapes[0] = (Shape*)circle_create("Small Circle", 2.5); shapes[1] = (Shape*)rectangle_create("Golden Rectangle", 4.0, 6.0); shapes[2] = (Shape*)triangle_create("Right Triangle", 3.0, 4.0, 5.0); shapes[3] = (Shape*)circle_create("Large Circle", 5.0); shapes[4] = (Shape*)rectangle_create("Square", 3.0, 3.0); // Polymorphic processing - same code works for all shapes printf("Processing shapes polymorphically:\n"); for (int i = 0; i < 5; i++) { shape_draw(shapes[i]); printf(" Area: %.2f, Perimeter: %.2f\n\n", shape_area(shapes[i]), shape_perimeter(shapes[i])); } // Clean up for (int i = 0; i < 5; i++) { shape_destroy(shapes[i]); } return 0; } Function Factories (Returning Function Pointers)
#include <stdio.h> #include <math.h> #include <stdlib.h> // ============================================================ // FUNCTION FACTORIES - FUNCTIONS THAT RETURN FUNCTION POINTERS // ============================================================ typedef double (*MathFunc)(double); // Basic math functions double square(double x) { return x * x; } double cube(double x) { return x * x * x; } double sqrt_func(double x) { return sqrt(x); } double identity(double x) { return x; } double reciprocal(double x) { return x != 0 ? 1.0 / x : 0; } double negate(double x) { return -x; } // Function factory - returns appropriate function based on operation MathFunc get_operation(char op) { switch (op) { case '^': return square; // Actually square, not power case '3': return cube; case 'r': return sqrt_func; case 'i': return identity; case '/': return reciprocal; case '-': return negate; default: return NULL; } } // Function factory with parameters - returns a configured function typedef struct { double a; // multiplier double b; // addend } LinearParams; double linear_func(double x, void *params) { LinearParams *p = (LinearParams*)params; return p->a * x + p->b; } // Factory that creates a closure-like function MathFunc create_linear(double a, double b) { // Allocate persistent parameters LinearParams *params = malloc(sizeof(LinearParams)); params->a = a; params->b = b; // We need a wrapper that captures the parameters // This is tricky in C - we'll use a global approach or pass params separately // For now, we'll demonstrate a different approach // Alternative: Return a function that uses global/static state // (Not shown here for simplicity) printf("Created linear function: %.2f*x + %.2f\n", a, b); return NULL; // Placeholder } // More practical: Function that returns a function based on string typedef enum { MATH_SIN, MATH_COS, MATH_TAN, MATH_LOG, MATH_EXP } MathFuncType; MathFunc get_math_function(MathFuncType type) { switch (type) { case MATH_SIN: return sin; case MATH_COS: return cos; case MATH_TAN: return tan; case MATH_LOG: return log; case MATH_EXP: return exp; default: return NULL; } } // Derivative approximator - returns function that approximates derivative MathFunc derivative(MathFunc f, double h) { // This would need to return a dynamically created function // In C, we need a wrapper function that uses a global/static context // This is a limitation - closures aren't directly supported // For demonstration, we'll create a static wrapper printf("Created derivative approximator with step %.6f\n", h); // In real code, you'd need a struct with function pointer + context return NULL; } // Better approach: Function + context struct typedef struct { MathFunc func; void *context; } FuncWithContext; double call_with_context(FuncWithContext *fc, double x) { // This would call fc->func with access to fc->context return 0; // Simplified } int main() { printf("=== Function Factories ===\n\n"); // Get functions from factory char ops[] = {'^', '3', 'r', 'i', '/', '-'}; double test_value = 4.0; for (int i = 0; i < 6; i++) { MathFunc f = get_operation(ops[i]); if (f) { printf("%c(%.2f) = %.4f\n", ops[i], test_value, f(test_value)); } } // Get math functions MathFuncType types[] = {MATH_SIN, MATH_COS, MATH_TAN}; double angle = M_PI / 4; // 45 degrees for (int i = 0; i < 3; i++) { MathFunc f = get_math_function(types[i]); if (f) { printf("math[%d](%.4f) = %.4f\n", i, angle, f(angle)); } } return 0; } State Machines with Function Pointers
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> // ============================================================ // ADVANCED STATE MACHINES WITH FUNCTION POINTERS // ============================================================ // Forward declarations typedef struct StateMachine StateMachine; typedef void (*StateFunc)(StateMachine*); // State machine structure struct StateMachine { StateFunc current_state; char *state_name; int data; int timer; void *context; // For additional data }; // ==================== TRAFFIC LIGHT EXAMPLE ==================== typedef struct { int cars_waiting; int pedestrians_waiting; int pedestrian_request; } TrafficContext; void red_state(StateMachine *sm) { TrafficContext *ctx = (TrafficContext*)sm->context; sm->state_name = "RED"; printf("🛑 RED - Cars stop"); if (ctx->pedestrians_waiting) { printf(" (🚶 Pedestrians crossing)"); } printf("\n"); sm->timer++; // Check for pedestrian request if (ctx->pedestrian_request && sm->timer >= 10) { sm->current_state = red_state; // Stay in red for pedestrians ctx->pedestrians_waiting = 1; ctx->pedestrian_request = 0; sm->timer = 0; } // Normal transition to green else if (sm->timer >= 30) { sm->current_state = green_state; sm->timer = 0; } } void green_state(StateMachine *sm) { TrafficContext *ctx = (TrafficContext*)sm->context; sm->state_name = "GREEN"; printf("🟢 GREEN - Cars go (%d cars waiting)\n", ctx->cars_waiting); sm->timer++; // Serve waiting cars if (ctx->cars_waiting > 0 && sm->timer % 5 == 0) { ctx->cars_waiting--; printf(" Car served, %d remaining\n", ctx->cars_waiting); } // Check for pedestrian or transition to yellow if (ctx->pedestrians_waiting && sm->timer >= 15) { sm->current_state = red_state; ctx->pedestrians_waiting = 0; sm->timer = 0; } else if (sm->timer >= 25) { sm->current_state = yellow_state; sm->timer = 0; } } void yellow_state(StateMachine *sm) { sm->state_name = "YELLOW"; printf("🟡 YELLOW - Prepare to stop\n"); sm->timer++; if (sm->timer >= 5) { sm->current_state = red_state; sm->timer = 0; } } // ==================== PARSER STATE MACHINE ==================== typedef enum { TOKEN_NONE, TOKEN_NUMBER, TOKEN_OP, TOKEN_END } TokenType; typedef struct { char *input; int pos; double result; char last_op; double current_number; } ParserContext; void parser_start(StateMachine *sm); void parser_number(StateMachine *sm); void parser_operator(StateMachine *sm); void parser_error(StateMachine *sm); void parser_end(StateMachine *sm); void parser_start(StateMachine *sm) { ParserContext *ctx = (ParserContext*)sm->context; sm->state_name = "START"; // Skip whitespace while (ctx->input[ctx->pos] == ' ') ctx->pos++; if (ctx->input[ctx->pos] >= '0' && ctx->input[ctx->pos] <= '9') { sm->current_state = parser_number; } else { sm->current_state = parser_error; } } void parser_number(StateMachine *sm) { ParserContext *ctx = (ParserContext*)sm->context; sm->state_name = "NUMBER"; // Parse number char *endptr; ctx->current_number = strtod(ctx->input + ctx->pos, &endptr); ctx->pos = endptr - ctx->input; // Apply operation if we have one pending if (ctx->last_op) { switch (ctx->last_op) { case '+': ctx->result += ctx->current_number; break; case '-': ctx->result -= ctx->current_number; break; case '*': ctx->result *= ctx->current_number; break; case '/': if (ctx->current_number != 0) ctx->result /= ctx->current_number; break; } ctx->last_op = 0; } else { ctx->result = ctx->current_number; } // Skip whitespace while (ctx->input[ctx->pos] == ' ') ctx->pos++; // Check next character char c = ctx->input[ctx->pos]; if (c == '+' || c == '-' || c == '*' || c == '/') { ctx->last_op = c; ctx->pos++; sm->current_state = parser_operator; } else if (c == '\0') { sm->current_state = parser_end; } else { sm->current_state = parser_error; } } void parser_operator(StateMachine *sm) { ParserContext *ctx = (ParserContext*)sm->context; sm->state_name = "OPERATOR"; // Skip whitespace while (ctx->input[ctx->pos] == ' ') ctx->pos++; if (ctx->input[ctx->pos] >= '0' && ctx->input[ctx->pos] <= '9') { sm->current_state = parser_number; } else if (ctx->input[ctx->pos] == '\0') { sm->current_state = parser_error; // Expression can't end with operator } else { sm->current_state = parser_error; } } void parser_error(StateMachine *sm) { ParserContext *ctx = (ParserContext*)sm->context; sm->state_name = "ERROR"; printf("Parse error at position %d: '%s'\n", ctx->pos, ctx->input + ctx->pos); ctx->result = 0; } void parser_end(StateMachine *sm) { sm->state_name = "END"; printf("Parse complete. Result = %.2f\n", ((ParserContext*)sm->context)->result); } // Generic state machine runner void run_state_machine(StateMachine *sm, int max_steps) { int step = 0; while (sm->current_state != NULL && step < max_steps) { sm->current_state(sm); step++; } } int main() { printf("=== Advanced State Machines ===\n\n"); // Traffic light example printf("--- Traffic Light ---\n"); TrafficContext traffic_ctx = { .cars_waiting = 5, .pedestrians_waiting = 0, .pedestrian_request = 0 }; StateMachine traffic_lights = { .current_state = red_state, .state_name = "RED", .timer = 0, .context = &traffic_ctx }; // Simulate 40 seconds of traffic light operation for (int i = 0; i < 40; i++) { printf("t=%2d: ", i); traffic_lights.current_state(&traffic_lights); // Random events if (rand() % 10 == 0) { traffic_ctx.cars_waiting++; printf(" (🚗 New car arrived, now %d waiting)\n", traffic_ctx.cars_waiting); } if (rand() % 15 == 0) { traffic_ctx.pedestrian_request = 1; printf(" (🚶 Pedestrian waiting to cross)\n"); } } // Parser example printf("\n--- Expression Parser ---\n"); char expression[] = "10 + 20 * 3 - 5 / 2"; ParserContext parser_ctx = { .input = expression, .pos = 0, .result = 0, .last_op = 0, .current_number = 0 }; StateMachine parser = { .current_state = parser_start, .state_name = "START", .context = &parser_ctx }; run_state_machine(&parser, 20); return 0; } Generic Algorithms with Function Pointers
#include <stdio.h> #include <stdlib.h> #include <string.h> // ============================================================ // GENERIC ALGORITHMS USING FUNCTION POINTERS // ============================================================ typedef int (*Comparator)(const void*, const void*); typedef void (*Printer)(const void*); typedef void (*Transformer)(void*); typedef int (*Predicate)(const void*); // Generic find if void* find_if(void *base, size_t count, size_t size, Predicate pred, void *context) { char *ptr = (char*)base; for (size_t i = 0; i < count; i++) { if (pred(ptr + i * size)) { return ptr + i * size; } } return NULL; } // Generic for each void for_each(void *base, size_t count, size_t size, Transformer trans) { char *ptr = (char*)base; for (size_t i = 0; i < count; i++) { trans(ptr + i * size); } } // Generic accumulate double accumulate(const void *base, size_t count, size_t size, double (*func)(const void*)) { double sum = 0; const char *ptr = (const char*)base; for (size_t i = 0; i < count; i++) { sum += func(ptr + i * size); } return sum; } // Generic filter (creates new array) void* filter(const void *base, size_t count, size_t size, Predicate pred, size_t *result_count) { // First pass - count matches size_t matches = 0; const char *src = (const char*)base; for (size_t i = 0; i < count; i++) { if (pred(src + i * size)) { matches++; } } if (matches == 0) { *result_count = 0; return NULL; } // Allocate result array void *result = malloc(matches * size); char *dst = (char*)result; // Copy matches for (size_t i = 0; i < count; i++) { if (pred(src + i * size)) { memcpy(dst, src + i * size, size); dst += size; } } *result_count = matches; return result; } // ==================== INTEGER SPECIFIC IMPLEMENTATIONS ==================== int int_compare(const void *a, const void *b) { return *(const int*)a - *(const int*)b; } void int_print(const void *elem) { printf("%d", *(const int*)elem); } void int_double(void *elem) { *(int*)elem *= 2; } int int_is_even(const void *elem) { return *(const int*)elem % 2 == 0; } double int_to_double(const void *elem) { return (double)*(const int*)elem; } // ==================== STRING SPECIFIC IMPLEMENTATIONS ==================== int string_compare(const void *a, const void *b) { return strcmp(*(const char**)a, *(const char**)b); } void string_print(const void *elem) { printf("\"%s\"", *(const char**)elem); } void string_uppercase(void *elem) { char *str = *(char**)elem; for (; *str; str++) { *str = toupper(*str); } } int string_length_gt(const void *elem, int len) { return strlen(*(const char**)elem) > len; } // Wrapper for predicate with context typedef struct { Predicate pred; void *context; } PredicateWithContext; int predicate_wrapper(const void *elem, void *ctx) { PredicateWithContext *pwc = (PredicateWithContext*)ctx; return pwc->pred(elem); } // ==================== PERSON STRUCTURE ==================== typedef struct { char name[50]; int age; double salary; } Person; void person_print(const void *elem) { const Person *p = (const Person*)elem; printf("%s (%d) $%.2f", p->name, p->age, p->salary); } int person_age_gt_30(const void *elem) { return ((const Person*)elem)->age > 30; } double person_salary_to_double(const void *elem) { return ((const Person*)elem)->salary; } int person_compare_by_age(const void *a, const void *b) { return ((const Person*)a)->age - ((const Person*)b)->age; } int person_compare_by_name(const void *a, const void *b) { return strcmp(((const Person*)a)->name, ((const Person*)b)->name); } int main() { printf("=== Generic Algorithms ===\n\n"); // Integer examples printf("--- Integer Operations ---\n"); int numbers[] = {5, 12, 7, 8, 3, 10, 15, 6, 9, 4}; size_t num_count = sizeof(numbers) / sizeof(numbers[0]); printf("Original: "); for_each(numbers, num_count, sizeof(int), int_print); printf("\n"); // Find first even number int *first_even = (int*)find_if(numbers, num_count, sizeof(int), int_is_even, NULL); if (first_even) { printf("First even number: %d\n", *first_even); } // Double all numbers for_each(numbers, num_count, sizeof(int), int_double); printf("After doubling: "); for_each(numbers, num_count, sizeof(int), int_print); printf("\n"); // Sum all numbers double sum = accumulate(numbers, num_count, sizeof(int), int_to_double); printf("Sum: %.0f\n", sum); // Filter even numbers size_t even_count; int *evens = (int*)filter(numbers, num_count, sizeof(int), int_is_even, &even_count); if (evens) { printf("Even numbers: "); for (size_t i = 0; i < even_count; i++) { printf("%d ", evens[i]); } printf("\n"); free(evens); } // String examples printf("\n--- String Operations ---\n"); char *fruits[] = {"apple", "banana", "cherry", "date", "elderberry"}; size_t fruit_count = sizeof(fruits) / sizeof(fruits[0]); printf("Original strings: "); for_each(fruits, fruit_count, sizeof(char*), string_print); printf("\n"); // Make uppercase for_each(fruits, fruit_count, sizeof(char*), string_uppercase); printf("Uppercase: "); for_each(fruits, fruit_count, sizeof(char*), string_print); printf("\n"); // Person examples printf("\n--- Person Operations ---\n"); Person people[] = { {"Alice", 25, 65000}, {"Bob", 35, 82000}, {"Charlie", 42, 95000}, {"Diana", 31, 71000}, {"Eve", 28, 58000} }; size_t person_count = sizeof(people) / sizeof(people[0]); printf("People:\n"); for (size_t i = 0; i < person_count; i++) { printf(" "); person_print(&people[i]); printf("\n"); } // Filter people over 30 size_t over30_count; Person *over30 = (Person*)filter(people, person_count, sizeof(Person), person_age_gt_30, &over30_count); if (over30) { printf("\nPeople over 30:\n"); for (size_t i = 0; i < over30_count; i++) { printf(" "); person_print(&over30[i]); printf("\n"); } free(over30); } // Total salary double total_salary = accumulate(people, person_count, sizeof(Person), person_salary_to_double); printf("\nTotal salary: $%.2f\n", total_salary); printf("Average salary: $%.2f\n", total_salary / person_count); return 0; } Closures and Captured Context
#include <stdio.h> #include <stdlib.h> #include <string.h> // ============================================================ // SIMULATING CLOSURES WITH CONTEXT STRUCTURES // ============================================================ // Generic function with context typedef struct { void (*func)(void*, void*); // Function that takes context and data void *context; // Captured variables } Closure; void closure_call(Closure *c, void *data) { c->func(c->context, data); } // ==================== COUNTER CLOSURE ==================== typedef struct { int count; int step; } CounterContext; void counter_func(void *ctx, void *data) { CounterContext *c = (CounterContext*)ctx; int *output = (int*)data; c->count += c->step; *output = c->count; } Closure* create_counter(int initial, int step) { CounterContext *ctx = malloc(sizeof(CounterContext)); ctx->count = initial; ctx->step = step; Closure *c = malloc(sizeof(Closure)); c->func = counter_func; c->context = ctx; return c; } // ==================== MULTIPLIER CLOSURE ==================== typedef struct { double factor; } MultiplierContext; void multiplier_func(void *ctx, void *data) { MultiplierContext *m = (MultiplierContext*)ctx; double *value = (double*)data; *value *= m->factor; } Closure* create_multiplier(double factor) { MultiplierContext *ctx = malloc(sizeof(MultiplierContext)); ctx->factor = factor; Closure *c = malloc(sizeof(Closure)); c->func = multiplier_func; c->context = ctx; return c; } // ==================== FILTER PREDICATE WITH CONTEXT ==================== typedef struct { int (*pred)(void*, void*); // Predicate that takes context and element void *context; } PredicateClosure; int predicate_closure_call(PredicateClosure *pc, void *elem) { return pc->pred(pc->context, elem); } // Example: greater than threshold typedef struct { double threshold; } GreaterThanContext; int greater_than_func(void *ctx, void *elem) { GreaterThanContext *g = (GreaterThanContext*)ctx; double *value = (double*)elem; return *value > g->threshold; } PredicateClosure* create_greater_than(double threshold) { GreaterThanContext *ctx = malloc(sizeof(GreaterThanContext)); ctx->threshold = threshold; PredicateClosure *pc = malloc(sizeof(PredicateClosure)); pc->pred = greater_than_func; pc->context = ctx; return pc; } // ==================== MAP FUNCTION WITH CONTEXT ==================== typedef struct { double (*map)(void*, void*); void *context; } MapClosure; double map_closure_call(MapClosure *mc, void *elem) { return mc->map(mc->context, elem); } // Example: add constant typedef struct { double constant; } AddConstantContext; double add_constant_func(void *ctx, void *elem) { AddConstantContext *a = (AddConstantContext*)ctx; double *value = (double*)elem; return *value + a->constant; } MapClosure* create_add_constant(double constant) { AddConstantContext *ctx = malloc(sizeof(AddConstantContext)); ctx->constant = constant; MapClosure *mc = malloc(sizeof(MapClosure)); mc->map = add_constant_func; mc->context = ctx; return mc; } int main() { printf("=== Simulating Closures ===\n\n"); // Counter closure Closure *counter = create_counter(0, 2); printf("Counter closure (step 2):\n"); for (int i = 0; i < 5; i++) { int value; closure_call(counter, &value); printf(" Call %d: %d\n", i + 1, value); } // Multiplier closure Closure *multiplier = create_multiplier(1.5); double num = 10.0; printf("\nMultiplier closure (factor 1.5):\n"); for (int i = 0; i < 3; i++) { closure_call(multiplier, &num); printf(" After call %d: %.2f\n", i + 1, num); } // Filter with context double numbers[] = {1.5, 3.7, 2.1, 5.8, 0.9, 4.2}; size_t count = sizeof(numbers) / sizeof(numbers[0]); PredicateClosure *greater_than = create_greater_than(3.0); printf("\nNumbers greater than 3.0:\n"); for (size_t i = 0; i < count; i++) { if (predicate_closure_call(greater_than, &numbers[i])) { printf(" %.2f\n", numbers[i]); } } // Map with context MapClosure *add_five = create_add_constant(5.0); printf("\nOriginal numbers + 5:\n"); for (size_t i = 0; i < count; i++) { double result = map_closure_call(add_five, &numbers[i]); printf(" %.2f → %.2f\n", numbers[i], result); } // Clean up free(counter->context); free(counter); free(multiplier->context); free(multiplier); free(greater_than->context); free(greater_than); free(add_five->context); free(add_five); return 0; } Pointer to Function Pointers (Double Indirection)
#include <stdio.h> #include <stdlib.h> // ============================================================ // POINTER TO FUNCTION POINTERS (DOUBLE INDIRECTION) // ============================================================ 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 != 0 ? a / b : 0; } // Function that modifies a function pointer void set_operation(Operation *op_ptr, char op) { switch (op) { case '+': *op_ptr = add; break; case '-': *op_ptr = subtract; break; case '*': *op_ptr = multiply; break; case '/': *op_ptr = divide; break; default: *op_ptr = NULL; } } // Array of function pointers Operation operations[4] = {add, subtract, multiply, divide}; // Pointer to array of function pointers Operation (*operations_ptr)[4] = &operations; // Function that returns a pointer to a function pointer Operation* get_operation_ptr(int index) { if (index >= 0 && index < 4) { return &operations[index]; } return NULL; } // Dynamic dispatch with function pointer table typedef struct { Operation *vtable; // Pointer to array of function pointers int data; } DynamicObject; int main() { printf("=== Pointer to Function Pointers ===\n\n"); Operation current_op = NULL; // Set function pointer through pointer to pointer printf("Setting operations through pointer:\n"); set_operation(¤t_op, '+'); printf(" 10 + 5 = %d\n", current_op(10, 5)); set_operation(¤t_op, '*'); printf(" 10 * 5 = %d\n", current_op(10, 5)); // Access array of function pointers printf("\nAccessing array of function pointers:\n"); for (int i = 0; i < 4; i++) { printf(" operations[%d] at %p\n", i, (void*)operations[i]); } // Get pointer to a specific function pointer Operation *op_ptr = get_operation_ptr(2); // multiply if (op_ptr) { printf("\nGot pointer to operation[2]: %p\n", (void*)*op_ptr); printf(" 7 * 6 = %d\n", (*op_ptr)(7, 6)); } // Modify through pointer op_ptr = get_operation_ptr(1); // subtract printf("\nBefore change: %d - %d = %d\n", 10, 3, (*op_ptr)(10, 3)); // Change the function pointer through the pointer *op_ptr = add; printf("After change: %d + %d = %d\n", 10, 3, (*op_ptr)(10, 3)); // Dynamic object with vtable pointer printf("\nDynamic object with vtable:\n"); DynamicObject obj; obj.vtable = operations; obj.data = 42; Operation *vtable = obj.vtable; printf(" obj.data = %d\n", obj.data); printf(" vtable[0] (add): %d\n", vtable[0](10, 5)); printf(" vtable[1] (subtract): %d\n", vtable[1](10, 5)); return 0; } Function Pointer Performance Optimization
#include <stdio.h> #include <time.h> // ============================================================ // PERFORMANCE OPTIMIZATION WITH FUNCTION POINTERS // ============================================================ // Direct call vs indirect call performance comparison typedef int (*MathFunc)(int); int square(int x) { return x * x; } int cube(int x) { return x * x * x; } int identity(int x) { return x; } // Inline function (compiler may inline) static inline int inline_square(int x) { return x * x; } // Function that uses direct calls int direct_sum(int *arr, int n) { int sum = 0; for (int i = 0; i < n; i++) { sum += square(arr[i]); // Direct call } return sum; } // Function that uses indirect calls (function pointer) int indirect_sum(int *arr, int n, MathFunc f) { int sum = 0; for (int i = 0; i < n; i++) { sum += f(arr[i]); // Indirect call through pointer } return sum; } // Function that uses branch prediction friendly dispatch typedef enum { OP_SQUARE, OP_CUBE, OP_IDENTITY } OpType; int dispatch_sum(int *arr, int n, OpType op) { int sum = 0; for (int i = 0; i < n; i++) { switch (op) { case OP_SQUARE: sum += square(arr[i]); break; case OP_CUBE: sum += cube(arr[i]); break; case OP_IDENTITY: sum += identity(arr[i]); break; } } return sum; } // Function pointer table for computed goto style (using function pointers) MathFunc op_table[] = {square, cube, identity}; int table_sum(int *arr, int n, int op_index) { int sum = 0; MathFunc f = op_table[op_index]; for (int i = 0; i < n; i++) { sum += f(arr[i]); } return sum; } int main() { printf("=== Performance Optimization ===\n\n"); const int SIZE = 10000000; int *arr = malloc(SIZE * sizeof(int)); for (int i = 0; i < SIZE; i++) { arr[i] = i % 100; } clock_t start, end; // Direct call start = clock(); int result1 = direct_sum(arr, SIZE); end = clock(); printf("Direct call: %.3f seconds\n", (double)(end - start) / CLOCKS_PER_SEC); // Indirect call (function pointer) start = clock(); int result2 = indirect_sum(arr, SIZE, square); end = clock(); printf("Indirect call: %.3f seconds\n", (double)(end - start) / CLOCKS_PER_SEC); // Dispatch (switch statement) start = clock(); int result3 = dispatch_sum(arr, SIZE, OP_SQUARE); end = clock(); printf("Dispatch switch: %.3f seconds\n", (double)(end - start) / CLOCKS_PER_SEC); // Table lookup start = clock(); int result4 = table_sum(arr, SIZE, 0); // 0 = square end = clock(); printf("Table dispatch: %.3f seconds\n", (double)(end - start) / CLOCKS_PER_SEC); printf("\nAll results: %d, %d, %d, %d\n", result1, result2, result3, result4); printf("\nPerformance notes:\n"); printf(" - Direct calls are fastest (can be inlined)\n"); printf(" - Indirect calls have small overhead (branch prediction)\n"); printf(" - Modern CPUs handle both well\n"); printf(" - Table dispatch can be as fast as direct for stable prediction\n"); free(arr); return 0; } Best Practices for Advanced Function Pointers
#include <stdio.h> #include <stdlib.h> #include <assert.h> // ============================================================ // BEST PRACTICES FOR ADVANCED FUNCTION POINTER USAGE // ============================================================ // 1. Always check for NULL before calling #define SAFE_CALL(func, ...) \ do { \ if (func != NULL) { \ func(__VA_ARGS__); \ } \ } while(0) // 2. Use typedefs for complex function pointer types typedef int (*Comparator)(const void*, const void*); typedef void (*ErrorHandler)(const char*); typedef void* (*Allocator)(size_t); // 3. Document function pointer expectations /** * Function pointer type for comparison operations * @param a First element to compare * @param b Second element to compare * @return negative if a<b, 0 if a==b, positive if a>b */ typedef int (*CompareFunc)(const void*, const void*); // 4. Use const for function pointers that shouldn't change void process_array(const int *arr, size_t size, const CompareFunc cmp) { // cmp cannot be reassigned inside function } // 5. Encapsulate function pointers with their context typedef struct { CompareFunc compare; void *context; } CompareWithContext; int compare_with_context(const void *a, const void *b, CompareWithContext *ctx) { // Use ctx->compare with access to ctx->context return 0; } // 6. Provide default implementations void default_error_handler(const char *msg) { fprintf(stderr, "Error: %s\n", msg); } typedef struct { ErrorHandler error_handler; Allocator allocator; // ... other function pointers } LibraryConfig; LibraryConfig library_config = { .error_handler = default_error_handler, .allocator = malloc }; // 7. Validate function pointers in debug builds void validate_comparator(CompareFunc cmp) { assert(cmp != NULL); // Additional validation in debug mode int test = cmp(&test, &test); assert(test == 0); // Should return 0 for equal elements } // 8. Function pointer arrays with bounds checking #define MAX_HANDLERS 10 typedef struct { int event_type; void (*handler)(void*); void *data; } EventHandler; EventHandler handlers[MAX_HANDLERS]; int handler_count = 0; int register_handler(int event_type, void (*handler)(void*), void *data) { if (handler_count >= MAX_HANDLERS) return -1; handlers[handler_count].event_type = event_type; handlers[handler_count].handler = handler; handlers[handler_count].data = data; handler_count++; return 0; } void dispatch_event(int event_type) { for (int i = 0; i < handler_count; i++) { if (handlers[i].event_type == event_type && handlers[i].handler != NULL) { handlers[i].handler(handlers[i].data); } } } // 9. Thread safety considerations #include <pthread.h> typedef struct { void (*func)(void*); void *arg; pthread_mutex_t lock; } ThreadSafeCallback; void ts_callback_call(ThreadSafeCallback *cb) { pthread_mutex_lock(&cb->lock); if (cb->func) { cb->func(cb->arg); } pthread_mutex_unlock(&cb->lock); } // 10. Function pointer versioning typedef struct { int version; void (*v1_func)(void); void (*v2_func)(int); // Newer versions add more function pointers } VersionedAPI; void call_versioned_api(VersionedAPI *api) { if (api->version >= 2 && api->v2_func) { api->v2_func(42); } else if (api->v1_func) { api->v1_func(); } } int main() { printf("=== Best Practices ===\n\n"); // Using SAFE_CALL macro ErrorHandler handler = NULL; SAFE_CALL(handler, "This won't crash"); handler = default_error_handler; SAFE_CALL(handler, "This will print"); // Event dispatch example register_handler(1, (void(*)(void*))printf, "Event 1 occurred\n"); register_handler(2, (void(*)(void*))printf, "Event 2 occurred\n"); dispatch_event(1); dispatch_event(2); dispatch_event(3); // No handler return 0; } Conclusion
Advanced function pointer techniques in C enable sophisticated programming paradigms that rival those found in higher-level languages. Key takeaways:
- Virtual Tables provide polymorphism and object-oriented behavior
- Dispatch Tables replace large switch statements for cleaner code
- Function Factories enable dynamic function creation
- State Machines become elegant with function pointer transitions
- Generic Algorithms work with any data type using function pointers
- Closures can be simulated with context structures
- Double Indirection allows modifying function pointers dynamically
- Performance considerations help optimize critical paths
Mastering these advanced techniques transforms C programming from simple procedural code to flexible, maintainable, and extensible systems that can adapt to changing requirements without sacrificing performance.
Complete C Programming Guide + Compilers Collection
1. C srand() Function – Understanding Seed Initialization
https://macronepal.com/understanding-the-c-srand-function
Explains how srand() initializes the pseudo-random number generator in C by setting a seed value. Using the same seed produces the same sequence, while time(NULL) gives different results each run.
2. C rand() Function Mechanics and Limitations
https://macronepal.com/c-rand-function-mechanics-and-limitations
Explains how rand() generates pseudo-random numbers between 0 and RAND_MAX, its deterministic nature, and limitations for security use cases.
3. C log() Function
https://macronepal.com/c-log-function-2
Covers natural logarithm calculation using <math.h> and its applications.
4. Mastering Date and Time in C
https://macronepal.com/mastering-date-and-time-in-c
Explains <time.h> functions like time(), clock(), difftime(), and struct tm.
5. Mastering time_t Type in C
https://macronepal.com/mastering-the-c-time_t-type-for-time-management
Explains time representation as seconds since Unix epoch and conversion functions.
6. C exp() Function
https://macronepal.com/c-exp-function-mechanics-and-implementation
Explains exponential function exp(x) and its scientific applications.
7. C log() Function (Alternate Guide)
https://macronepal.com/c-log-function
Comparison of log() and log10() with usage examples.
8. C log10() Function
https://macronepal.com/mastering-the-log10-function-in-c
Explains base-10 logarithm for engineering and scientific applications.
9. C tan() Function
https://macronepal.com/understanding-the-c-tan-function
Explains tangent function and radian-based calculations.
10. Random Numbers in C (Secure vs Predictable)
https://macronepal.com/mastering-c-random-numbers-for-secure-and-predictable-applications
Explains difference between rand() and secure randomness methods.
11. Free Online C Compiler
https://macronepal.com/free-online-c-code-compiler-2
Browser-based compiler for testing C programs instantly.
C Functions, Arguments, Parameters & Flow
Mastering Functions in C – Complete Guide
https://macronepal.com/c/mastering-functions-in-c-a-complete-guide/
Covers function structure, modular programming, and real-world usage.
Function Arguments in C
https://macronepal.com/c-function-arguments/
Explains how arguments are passed and used in function calls.
Function Parameters in C
https://macronepal.com/c-function-parameters/
Explains defining inputs for functions and matching them with arguments.
Function Declarations in C
https://macronepal.com/c-function-declarations-syntax-rules-and-best-practices/
Covers prototypes, syntax rules, and best practices.
Function Calls in C
https://macronepal.com/understanding-function-calls-in-c-syntax-mechanics-and-best-practices/
Explains execution flow and parameter handling during function calls.
Void Functions in C
https://macronepal.com/understanding-void-functions-in-c-syntax-patterns-and-best-practices/
Explains functions that do not return values.
Return Values in C
https://macronepal.com/c-return-values-mechanics-types-and-best-practices/
Explains different return types and how functions return results.
Pass-by-Value in C
https://macronepal.com/aws/understanding-pass-by-value-in-c-mechanics-implications-and-best-practices/
Explains how copies of variables are passed into functions.
Pass-by-Reference in C
https://macronepal.com/c/understanding-pass-by-reference-in-c-pointers-semantics-and-safe-practices/
Explains using pointers to modify original variables.
C strstr() Function
https://macronepal.com/aws/c-strstr-function/
Explains substring search inside strings in C.
C Preprocessor & Macros
https://macronepal.com/mastering-c-variadic-macros-for-flexible-debugging/
https://macronepal.com/mastering-the-stdc-macro-in-c/
https://macronepal.com/c-time-macro-mechanics-and-usage/
https://macronepal.com/understanding-the-c-date-macro/
https://macronepal.com/c-file-type/
https://macronepal.com/mastering-c-line-macro-for-debugging-and-diagnostics/
https://macronepal.com/mastering-predefined-macros-in-c/
https://macronepal.com/c-error-directive-mechanics-and-usage/
https://macronepal.com/understanding-the-c-pragma-directive/
https://macronepal.com/c-include-directive/
C Structures, Memory, Scope & Linkage
https://macronepal.com/mastering-structures-in-c/
https://macronepal.com/c-structure-declaration-mechanics-and-usage/
https://macronepal.com/c-structure-initialization-mechanics-and-best-practices/
https://macronepal.com/mastering-c-structure-member-access-for-reliable-data-handling/
https://macronepal.com/c-nested-structures/
https://macronepal.com/mastering-arrays-of-structures-in-c/
https://macronepal.com/c-structure-pointers-mechanics-and-implementation/
https://macronepal.com/understanding-c-structure-parameter-passing-mechanics/
https://macronepal.com/mastering-c-returning-structures-for-efficient-data-flow/
https://macronepal.com/c-self-referential-structures/
https://macronepal.com/mastering-structure-alignment-in-c/
https://macronepal.com/c-structure-padding-mechanics-and-optimization/
https://macronepal.com/understanding-c-flexible-array-members-mechanics-and-usage/
https://macronepal.com/mastering-c-anonymous-structures-for-flattened-data-layouts/
https://macronepal.com/c-unions/
https://macronepal.com/mastering-c-name-mangling-and-symbol-decoration/
https://macronepal.com/c-no-linkage-mechanics-and-scope-isolation/
https://macronepal.com/understanding-c-internal-linkage-mechanics-and-architecture/
C Scope, Storage Classes & Typedef
https://macronepal.com/mastering-function-prototype-scope-in-c/
https://macronepal.com/c-function-scope-mechanics-and-visibility/
https://macronepal.com/understanding-c-file-scope-mechanics-and-architecture/
https://macronepal.com/mastering-c-scope-rules-for-predictable-name-resolution/
https://macronepal.com/c-scope-rules/
https://macronepal.com/mastering-c-register-storage-class-for-historical-context-and-modern-alternatives/
https://macronepal.com/mastering-_thread_local-in-c/
https://macronepal.com/c-extern-storage-class-mechanics-and-usage/
https://macronepal.com/understanding-the-c-static-storage-class-mechanics-and-usage/
https://macronepal.com/c-auto-storage-class/
https://macronepal.com/c-typedef-with-pointers/
Extra Articles
https://macronepal.com/13757-2/
https://macronepal.com/13748-2/
https://macronepal.com/13747-2/
https://macronepal.com/13746-2/
https://macronepal.com/13745-2/
https://macronepal.com/13708-2/
https://macronepal.com/13707-2/
https://macronepal.com/13702-2/
Online Compilers
https://macronepal.com/free-html-online-code-compiler/
https://macronepal.com/free-online-python-code-compiler/
https://macronepal.com/free-online-python2-code-compiler/
https://macronepal.com/free-online-java-code-compiler/
https://macronepal.com/free-online-javascript-code-compiler/
https://macronepal.com/free-online-node-js-code-compiler/
https://macronepal.com/free-online-c-code-compiler/
https://macronepal.com/free-online-c-code-compiler-2/
https://macronepal.com/free-online-c-code-compiler-3/
https://macronepal.com/free-online-php-code-compiler/
https://macronepal.com/free-online-ruby-code-compiler/
https://macronepal.com/free-online-perl-code-compiler/
https://macronepal.com/free-online-lua-code-compiler/
https://macronepal.com/free-online-tcl-code-compiler/
https://macronepal.com/free-online-groovy-code-compiler/
https://macronepal.com/free-online-j-shell-code-compiler/
https://macronepal.com/free-online-haskell-code-compiler/
https://macronepal.com/free-online-scala-code-compiler/
https://macronepal.com/free-online-common-lisp-code-compiler/
https://macronepal.com/free-online-d-code-compiler/
https://macronepal.com/free-online-ada-code-compiler/
https://macronepal.com/free-erlang-code-compiler/
https://macronepal.com/free-online-assembly-code-compiler/
