Introduction to Function Pointers in Arrays
Function pointers in arrays allow you to store addresses of functions in an array, enabling dynamic function dispatch, state machines, command processors, and polymorphic behavior. This powerful technique is essential for writing flexible and extensible C programs.
Architecture Overview
Function Pointers Array Architecture ├── Declaration │ ├── return_type (*array_name[size])(parameter_types) │ └── typedef for cleaner syntax ├── Storage │ ├── Array of function addresses │ ├── Index-based access │ └── Dynamic dispatch └── Applications ├── Command processors ├── State machines ├── Jump tables └── Plugin systems
Basic Function Pointers in Arrays
1. Simple Function Pointer Array
#include <stdio.h>
// Basic functions
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) {
if (b == 0) return 0;
return a / b;
}
int modulus(int a, int b) {
if (b == 0) return 0;
return a % b;
}
int main() {
// Array of function pointers
int (*operations[])(int, int) = {
add, subtract, multiply, divide, modulus
};
char* op_names[] = {"Add", "Subtract", "Multiply", "Divide", "Modulus"};
int num_ops = sizeof(operations) / sizeof(operations[0]);
int a = 20, b = 5;
printf("=== Basic Function Pointer Array ===\n");
printf("a = %d, b = %d\n\n", a, b);
for (int i = 0; i < num_ops; i++) {
int result = operations[i](a, b);
printf("%s: %d\n", op_names[i], result);
}
return 0;
}
Output:
=== Basic Function Pointer Array === a = 20, b = 5 Add: 25 Subtract: 15 Multiply: 100 Divide: 4 Modulus: 0
2. Using typedef for Cleaner Syntax
#include <stdio.h>
// Define function pointer type
typedef int (*MathOperation)(int, int);
// Functions
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; }
int main() {
// Array using typedef
MathOperation operations[] = {add, subtract, multiply, divide};
const char* names[] = {"Add", "Subtract", "Multiply", "Divide"};
int x = 30, y = 6;
printf("=== Using typedef ===\n\n");
for (int i = 0; i < 4; i++) {
printf("%s(%d, %d) = %d\n", names[i], x, y, operations[i](x, y));
}
// Dynamic dispatch based on input
int choice;
printf("\nEnter operation (0-3): ");
scanf("%d", &choice);
if (choice >= 0 && choice < 4) {
printf("Result: %d\n", operations[choice](x, y));
} else {
printf("Invalid choice\n");
}
return 0;
}
Command Processor with Function Pointer Arrays
1. Simple Command Processor
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// Command functions
void cmd_help(void) {
printf("Available commands:\n");
printf(" help - Show this help\n");
printf(" clear - Clear screen\n");
printf(" exit - Exit program\n");
printf(" echo - Echo arguments\n");
printf(" calc - Simple calculator\n");
}
void cmd_clear(void) {
printf("\033[2J\033[1;1H"); // ANSI escape sequence to clear screen
printf("Screen cleared\n");
}
void cmd_exit(void) {
printf("Goodbye!\n");
}
void cmd_echo(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
}
void cmd_calc(int argc, char* argv[]) {
if (argc != 4) {
printf("Usage: calc <num1> <op> <num2>\n");
return;
}
double a = atof(argv[1]);
double b = atof(argv[3]);
char op = argv[2][0];
switch (op) {
case '+': printf("%.2f + %.2f = %.2f\n", a, b, a + b); break;
case '-': printf("%.2f - %.2f = %.2f\n", a, b, a - b); break;
case '*': printf("%.2f * %.2f = %.2f\n", a, b, a * b); break;
case '/':
if (b != 0) printf("%.2f / %.2f = %.2f\n", a, b, a / b);
else printf("Division by zero!\n");
break;
default: printf("Unknown operator: %c\n", op);
}
}
// Command structure
typedef struct {
const char* name;
void (*func)(int, char*[]);
const char* description;
} Command;
// Command array
Command commands[] = {
{"help", cmd_help, "Show this help"},
{"clear", cmd_clear, "Clear screen"},
{"exit", cmd_exit, "Exit program"},
{"echo", cmd_echo, "Echo arguments"},
{"calc", cmd_calc, "Simple calculator"}
};
int num_commands = sizeof(commands) / sizeof(commands[0]);
// Find and execute command
int execute_command(char* input) {
// Parse command line
char* argv[10];
int argc = 0;
char* token = strtok(input, " \n");
while (token != NULL && argc < 10) {
argv[argc++] = token;
token = strtok(NULL, " \n");
}
if (argc == 0) return 1;
// Find command
for (int i = 0; i < num_commands; i++) {
if (strcmp(argv[0], commands[i].name) == 0) {
commands[i].func(argc, argv);
return 1;
}
}
printf("Unknown command: %s\n", argv[0]);
return 1;
}
int main() {
char input[256];
printf("=== Command Processor ===\n");
printf("Type 'help' for commands, 'exit' to quit\n\n");
while (1) {
printf("> ");
fflush(stdout);
if (fgets(input, sizeof(input), stdin) == NULL) {
break;
}
if (!execute_command(input)) {
break;
}
}
return 0;
}
2. Advanced Command Processor with Help System
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
// Function pointer type for commands
typedef void (*CommandFunc)(int, char**);
// Command structure with metadata
typedef struct {
const char* name;
CommandFunc func;
const char* usage;
const char* description;
int min_args;
int max_args;
} Command;
// Command functions
void cmd_add(int argc, char** argv) {
if (argc < 3) {
printf("Error: Need at least two numbers\n");
return;
}
double sum = 0;
for (int i = 1; i < argc; i++) {
sum += atof(argv[i]);
}
printf("Sum = %.2f\n", sum);
}
void cmd_multiply(int argc, char** argv) {
if (argc < 3) {
printf("Error: Need at least two numbers\n");
return;
}
double product = 1;
for (int i = 1; i < argc; i++) {
product *= atof(argv[i]);
}
printf("Product = %.2f\n", product);
}
void cmd_average(int argc, char** argv) {
if (argc < 3) {
printf("Error: Need at least two numbers\n");
return;
}
double sum = 0;
for (int i = 1; i < argc; i++) {
sum += atof(argv[i]);
}
printf("Average = %.2f\n", sum / (argc - 1));
}
void cmd_min(int argc, char** argv) {
if (argc < 3) {
printf("Error: Need at least two numbers\n");
return;
}
double min_val = atof(argv[1]);
for (int i = 2; i < argc; i++) {
double val = atof(argv[i]);
if (val < min_val) min_val = val;
}
printf("Minimum = %.2f\n", min_val);
}
void cmd_max(int argc, char** argv) {
if (argc < 3) {
printf("Error: Need at least two numbers\n");
return;
}
double max_val = atof(argv[1]);
for (int i = 2; i < argc; i++) {
double val = atof(argv[i]);
if (val > max_val) max_val = val;
}
printf("Maximum = %.2f\n", max_val);
}
void cmd_help(int argc, char** argv, const Command* commands, int num_commands) {
if (argc == 1) {
// General help
printf("\nAvailable commands:\n");
for (int i = 0; i < num_commands; i++) {
printf(" %-12s - %s\n", commands[i].name, commands[i].description);
}
printf("\nType 'help <command>' for detailed help on a specific command.\n");
} else {
// Command-specific help
for (int i = 0; i < num_commands; i++) {
if (strcmp(argv[1], commands[i].name) == 0) {
printf("\nCommand: %s\n", commands[i].name);
printf("Usage: %s\n", commands[i].usage);
printf("Description: %s\n", commands[i].description);
printf("Arguments: %d to %d\n",
commands[i].min_args - 1,
commands[i].max_args - 1);
return;
}
}
printf("Unknown command: %s\n", argv[1]);
}
}
// Command table
Command command_table[] = {
{"add", cmd_add, "add <num1> <num2> ...",
"Add multiple numbers", 3, 10},
{"multiply", cmd_multiply, "multiply <num1> <num2> ...",
"Multiply multiple numbers", 3, 10},
{"average", cmd_average, "average <num1> <num2> ...",
"Calculate average of numbers", 3, 10},
{"min", cmd_min, "min <num1> <num2> ...",
"Find minimum value", 3, 10},
{"max", cmd_max, "max <num1> <num2> ...",
"Find maximum value", 3, 10}
};
int num_commands = sizeof(command_table) / sizeof(command_table[0]);
// Wrapper for help command
void cmd_help_wrapper(int argc, char** argv) {
cmd_help(argc, argv, command_table, num_commands);
}
// Add help to command table
Command all_commands[] = {
{"add", cmd_add, "add <num1> <num2> ...",
"Add multiple numbers", 3, 10},
{"multiply", cmd_multiply, "multiply <num1> <num2> ...",
"Multiply multiple numbers", 3, 10},
{"average", cmd_average, "average <num1> <num2> ...",
"Calculate average of numbers", 3, 10},
{"min", cmd_min, "min <num1> <num2> ...",
"Find minimum value", 3, 10},
{"max", cmd_max, "max <num1> <num2> ...",
"Find maximum value", 3, 10},
{"help", cmd_help_wrapper, "help [command]",
"Show this help", 1, 2}
};
int total_commands = sizeof(all_commands) / sizeof(all_commands[0]);
int main() {
char input[256];
char* argv[20];
printf("=== Advanced Command Processor ===\n");
printf("Type 'help' for available commands\n\n");
while (1) {
printf("calc> ");
fflush(stdout);
if (fgets(input, sizeof(input), stdin) == NULL) {
break;
}
// Parse command line
int argc = 0;
char* token = strtok(input, " \n");
while (token != NULL && argc < 20) {
argv[argc++] = token;
token = strtok(NULL, " \n");
}
if (argc == 0) continue;
// Check for exit
if (strcmp(argv[0], "exit") == 0) {
printf("Goodbye!\n");
break;
}
// Find and execute command
int found = 0;
for (int i = 0; i < total_commands; i++) {
if (strcmp(argv[0], all_commands[i].name) == 0) {
if (argc >= all_commands[i].min_args &&
argc <= all_commands[i].max_args) {
all_commands[i].func(argc, argv);
} else {
printf("Error: Invalid number of arguments\n");
printf("Usage: %s\n", all_commands[i].usage);
}
found = 1;
break;
}
}
if (!found) {
printf("Unknown command: %s\n", argv[0]);
}
}
return 0;
}
State Machine with Function Pointer Arrays
1. Simple State Machine
#include <stdio.h>
#include <stdbool.h>
// State definitions
typedef enum {
STATE_IDLE,
STATE_RUNNING,
STATE_PAUSED,
STATE_STOPPED,
STATE_ERROR,
NUM_STATES
} State;
// Event definitions
typedef enum {
EVENT_START,
EVENT_PAUSE,
EVENT_RESUME,
EVENT_STOP,
EVENT_ERROR,
EVENT_RESET,
NUM_EVENTS
} Event;
// Function pointer type for state handlers
typedef void (*StateHandler)(Event event);
// State handler functions
void idle_handler(Event event) {
switch (event) {
case EVENT_START:
printf("Idle -> Starting...\n");
// Transition to RUNNING
break;
case EVENT_STOP:
printf("Idle -> Already stopped\n");
break;
default:
printf("Idle: Ignoring event %d\n", event);
}
}
void running_handler(Event event) {
switch (event) {
case EVENT_PAUSE:
printf("Running -> Pausing...\n");
break;
case EVENT_STOP:
printf("Running -> Stopping...\n");
break;
case EVENT_ERROR:
printf("Running -> Error occurred!\n");
break;
default:
printf("Running: Ignoring event %d\n", event);
}
}
void paused_handler(Event event) {
switch (event) {
case EVENT_RESUME:
printf("Paused -> Resuming...\n");
break;
case EVENT_STOP:
printf("Paused -> Stopping...\n");
break;
default:
printf("Paused: Ignoring event %d\n", event);
}
}
void stopped_handler(Event event) {
switch (event) {
case EVENT_START:
printf("Stopped -> Starting...\n");
break;
case EVENT_RESET:
printf("Stopped -> Resetting to idle...\n");
break;
default:
printf("Stopped: Ignoring event %d\n", event);
}
}
void error_handler(Event event) {
switch (event) {
case EVENT_RESET:
printf("Error -> Resetting to idle...\n");
break;
default:
printf("Error: Ignoring event %d\n", event);
}
}
// State machine structure
typedef struct {
State current_state;
StateHandler handlers[NUM_STATES];
State transitions[NUM_STATES][NUM_EVENTS];
} StateMachine;
// Initialize state machine
void init_state_machine(StateMachine* sm) {
sm->current_state = STATE_IDLE;
// Set up handlers
sm->handlers[STATE_IDLE] = idle_handler;
sm->handlers[STATE_RUNNING] = running_handler;
sm->handlers[STATE_PAUSED] = paused_handler;
sm->handlers[STATE_STOPPED] = stopped_handler;
sm->handlers[STATE_ERROR] = error_handler;
// Initialize all transitions to same state (no transition)
for (int i = 0; i < NUM_STATES; i++) {
for (int j = 0; j < NUM_EVENTS; j++) {
sm->transitions[i][j] = i;
}
}
// Define transitions
sm->transitions[STATE_IDLE][EVENT_START] = STATE_RUNNING;
sm->transitions[STATE_RUNNING][EVENT_PAUSE] = STATE_PAUSED;
sm->transitions[STATE_RUNNING][EVENT_STOP] = STATE_STOPPED;
sm->transitions[STATE_RUNNING][EVENT_ERROR] = STATE_ERROR;
sm->transitions[STATE_PAUSED][EVENT_RESUME] = STATE_RUNNING;
sm->transitions[STATE_PAUSED][EVENT_STOP] = STATE_STOPPED;
sm->transitions[STATE_STOPPED][EVENT_START] = STATE_RUNNING;
sm->transitions[STATE_STOPPED][EVENT_RESET] = STATE_IDLE;
sm->transitions[STATE_ERROR][EVENT_RESET] = STATE_IDLE;
}
// Process event
void process_event(StateMachine* sm, Event event) {
printf("Event: %d, Current state: %d\n", event, sm->current_state);
// Call current state handler
sm->handlers[sm->current_state](event);
// Transition to new state
State new_state = sm->transitions[sm->current_state][event];
if (new_state != sm->current_state) {
printf("State transition: %d -> %d\n", sm->current_state, new_state);
sm->current_state = new_state;
}
}
int main() {
StateMachine sm;
init_state_machine(&sm);
printf("=== State Machine with Function Pointers ===\n\n");
// Simulate some events
process_event(&sm, EVENT_START);
printf("\n");
process_event(&sm, EVENT_PAUSE);
printf("\n");
process_event(&sm, EVENT_RESUME);
printf("\n");
process_event(&sm, EVENT_ERROR);
printf("\n");
process_event(&sm, EVENT_RESET);
printf("\n");
process_event(&sm, EVENT_START);
printf("\n");
process_event(&sm, EVENT_STOP);
printf("\n");
return 0;
}
2. Table-Driven State Machine
#include <stdio.h>
#include <stdbool.h>
// State and event definitions
typedef enum {
STATE_OFF,
STATE_ON,
STATE_SLEEP,
STATE_STANDBY,
NUM_STATES
} State;
typedef enum {
EV_POWER,
EV_WAKE,
EV_SLEEP,
EV_TIMEOUT,
NUM_EVENTS
} Event;
// Function pointer for action functions
typedef void (*Action)(void);
// Action functions
void action_none(void) { }
void action_power_on(void) { printf(" Powering on...\n"); }
void action_power_off(void) { printf(" Powering off...\n"); }
void action_enter_sleep(void) { printf(" Entering sleep mode...\n"); }
void action_exit_sleep(void) { printf(" Exiting sleep mode...\n"); }
void action_enter_standby(void) { printf(" Entering standby...\n"); }
void action_exit_standby(void) { printf(" Exiting standby...\n"); }
void action_timeout_warning(void) { printf(" Timeout warning!\n"); }
// Transition structure
typedef struct {
State next_state;
Action action;
} Transition;
// State transition table
Transition transition_table[NUM_STATES][NUM_EVENTS] = {
// STATE_OFF
{
{STATE_ON, action_power_on}, // EV_POWER
{STATE_OFF, action_none}, // EV_WAKE
{STATE_OFF, action_none}, // EV_SLEEP
{STATE_OFF, action_none} // EV_TIMEOUT
},
// STATE_ON
{
{STATE_OFF, action_power_off}, // EV_POWER
{STATE_ON, action_none}, // EV_WAKE
{STATE_SLEEP, action_enter_sleep}, // EV_SLEEP
{STATE_STANDBY, action_enter_standby} // EV_TIMEOUT
},
// STATE_SLEEP
{
{STATE_OFF, action_power_off}, // EV_POWER
{STATE_ON, action_exit_sleep}, // EV_WAKE
{STATE_SLEEP, action_none}, // EV_SLEEP
{STATE_SLEEP, action_none} // EV_TIMEOUT
},
// STATE_STANDBY
{
{STATE_OFF, action_power_off}, // EV_POWER
{STATE_ON, action_exit_standby}, // EV_WAKE
{STATE_STANDBY, action_none}, // EV_SLEEP
{STATE_STANDBY, action_timeout_warning} // EV_TIMEOUT
}
};
// State names for printing
const char* state_names[] = {
"OFF", "ON", "SLEEP", "STANDBY"
};
const char* event_names[] = {
"POWER", "WAKE", "SLEEP", "TIMEOUT"
};
typedef struct {
State current_state;
} Device;
void process_event_table(Device* dev, Event event) {
printf("Event: %s, Current state: %s\n",
event_names[event], state_names[dev->current_state]);
Transition t = transition_table[dev->current_state][event];
// Execute action
t.action();
// Transition to new state
if (t.next_state != dev->current_state) {
printf(" Transition: %s -> %s\n",
state_names[dev->current_state],
state_names[t.next_state]);
dev->current_state = t.next_state;
}
}
int main() {
Device dev = {STATE_OFF};
printf("=== Table-Driven State Machine ===\n\n");
process_event_table(&dev, EV_POWER);
printf("\n");
process_event_table(&dev, EV_SLEEP);
printf("\n");
process_event_table(&dev, EV_WAKE);
printf("\n");
process_event_table(&dev, EV_TIMEOUT);
printf("\n");
process_event_table(&dev, EV_POWER);
printf("\n");
return 0;
}
Polymorphic Behavior with Function Pointer Arrays
1. Shape Drawing Example
#include <stdio.h>
#include <math.h>
// Function pointer types
typedef void (*DrawFunc)(void*);
typedef double (*AreaFunc)(void*);
typedef void (*DestroyFunc)(void*);
// Base shape structure
typedef struct {
DrawFunc draw;
AreaFunc area;
DestroyFunc destroy;
} ShapeVTable;
typedef struct {
ShapeVTable* vtable;
} Shape;
// Circle
typedef struct {
Shape base;
double radius;
} Circle;
void circle_draw(void* obj) {
Circle* circle = (Circle*)obj;
printf("Drawing circle with radius %.2f\n", circle->radius);
}
double circle_area(void* obj) {
Circle* circle = (Circle*)obj;
return M_PI * circle->radius * circle->radius;
}
void circle_destroy(void* obj) {
printf("Destroying circle\n");
free(obj);
}
Circle* circle_create(double radius) {
static ShapeVTable circle_vtable = {
circle_draw,
circle_area,
circle_destroy
};
Circle* circle = malloc(sizeof(Circle));
circle->base.vtable = &circle_vtable;
circle->radius = radius;
return circle;
}
// Rectangle
typedef struct {
Shape base;
double width;
double height;
} Rectangle;
void rectangle_draw(void* obj) {
Rectangle* rect = (Rectangle*)obj;
printf("Drawing rectangle %.2f x %.2f\n", rect->width, rect->height);
}
double rectangle_area(void* obj) {
Rectangle* rect = (Rectangle*)obj;
return rect->width * rect->height;
}
void rectangle_destroy(void* obj) {
printf("Destroying rectangle\n");
free(obj);
}
Rectangle* rectangle_create(double width, double height) {
static ShapeVTable rectangle_vtable = {
rectangle_draw,
rectangle_area,
rectangle_destroy
};
Rectangle* rect = malloc(sizeof(Rectangle));
rect->base.vtable = &rectangle_vtable;
rect->width = width;
rect->height = height;
return rect;
}
// Array of shapes
int main() {
Shape* shapes[4];
shapes[0] = (Shape*)circle_create(5.0);
shapes[1] = (Shape*)rectangle_create(4.0, 6.0);
shapes[2] = (Shape*)circle_create(3.5);
shapes[3] = (Shape*)rectangle_create(2.0, 8.0);
printf("=== Polymorphic Shapes ===\n\n");
for (int i = 0; i < 4; i++) {
printf("Shape %d:\n", i + 1);
shapes[i]->vtable->draw(shapes[i]);
printf(" Area: %.2f\n", shapes[i]->vtable->area(shapes[i]));
printf("\n");
}
// Cleanup
for (int i = 0; i < 4; i++) {
shapes[i]->vtable->destroy(shapes[i]);
}
return 0;
}
2. Plugin System
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h> // For dynamic loading (Linux)
// Plugin function types
typedef int (*PluginInitFunc)(void);
typedef int (*PluginProcessFunc)(const char* input, char* output, int max_len);
typedef void (*PluginCleanupFunc)(void);
// Plugin structure
typedef struct {
char name[50];
void* handle; // Library handle
PluginInitFunc init;
PluginProcessFunc process;
PluginCleanupFunc cleanup;
int initialized;
} Plugin;
// Built-in plugins
int upper_init(void) { printf(" Upper plugin initialized\n"); return 1; }
int upper_process(const char* input, char* output, int max_len) {
int i = 0;
while (input[i] && i < max_len - 1) {
output[i] = toupper(input[i]);
i++;
}
output[i] = '\0';
return i;
}
void upper_cleanup(void) { printf(" Upper plugin cleaned up\n"); }
int lower_init(void) { printf(" Lower plugin initialized\n"); return 1; }
int lower_process(const char* input, char* output, int max_len) {
int i = 0;
while (input[i] && i < max_len - 1) {
output[i] = tolower(input[i]);
i++;
}
output[i] = '\0';
return i;
}
void lower_cleanup(void) { printf(" Lower plugin cleaned up\n"); }
int reverse_init(void) { printf(" Reverse plugin initialized\n"); return 1; }
int reverse_process(const char* input, char* output, int max_len) {
int len = strlen(input);
if (len >= max_len) len = max_len - 1;
for (int i = 0; i < len; i++) {
output[i] = input[len - 1 - i];
}
output[len] = '\0';
return len;
}
void reverse_cleanup(void) { printf(" Reverse plugin cleaned up\n"); }
// Plugin registry
Plugin plugins[10];
int plugin_count = 0;
// Register a plugin
void register_plugin(const char* name,
PluginInitFunc init,
PluginProcessFunc process,
PluginCleanupFunc cleanup) {
if (plugin_count >= 10) {
printf("Cannot register more plugins\n");
return;
}
strcpy(plugins[plugin_count].name, name);
plugins[plugin_count].init = init;
plugins[plugin_count].process = process;
plugins[plugin_count].cleanup = cleanup;
plugins[plugin_count].initialized = 0;
plugin_count++;
printf("Registered plugin: %s\n", name);
}
// Initialize all plugins
void init_plugins() {
for (int i = 0; i < plugin_count; i++) {
if (plugins[i].init && plugins[i].init()) {
plugins[i].initialized = 1;
}
}
}
// Cleanup all plugins
void cleanup_plugins() {
for (int i = 0; i < plugin_count; i++) {
if (plugins[i].initialized && plugins[i].cleanup) {
plugins[i].cleanup();
}
}
}
// Find plugin by name
Plugin* find_plugin(const char* name) {
for (int i = 0; i < plugin_count; i++) {
if (strcmp(plugins[i].name, name) == 0) {
return &plugins[i];
}
}
return NULL;
}
int main() {
// Register built-in plugins
register_plugin("upper", upper_init, upper_process, upper_cleanup);
register_plugin("lower", lower_init, lower_process, lower_cleanup);
register_plugin("reverse", reverse_init, reverse_process, reverse_cleanup);
printf("\n=== Plugin System ===\n\n");
// Initialize plugins
init_plugins();
// Test plugins
const char* test_string = "Hello World!";
char output[100];
for (int i = 0; i < plugin_count; i++) {
printf("\nTesting plugin '%s':\n", plugins[i].name);
printf(" Input: %s\n", test_string);
if (plugins[i].process) {
int len = plugins[i].process(test_string, output, sizeof(output));
printf(" Output: %s (%d chars)\n", output, len);
}
}
// Cleanup
printf("\nCleaning up...\n");
cleanup_plugins();
return 0;
}
Sorting with Function Pointer Arrays
1. Generic Sorting with Different Comparators
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Comparison function types
typedef int (*CompareFunc)(const void*, const void*);
// Integer comparison functions
int compare_int_asc(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_int_abs(const void* a, const void* b) {
int abs_a = abs(*(int*)a);
int abs_b = abs(*(int*)b);
return abs_a - abs_b;
}
// String comparison functions
int compare_string_asc(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
int compare_string_desc(const void* a, const void* b) {
return strcmp(*(const char**)b, *(const char**)a);
}
int compare_string_length(const void* a, const void* b) {
const char* sa = *(const char**)a;
const char* sb = *(const char**)b;
return strlen(sa) - strlen(sb);
}
// Structure to hold sort options
typedef struct {
const char* name;
CompareFunc func;
const char* description;
} SortOption;
// Array of sort options
SortOption sort_options[] = {
{"int_asc", compare_int_asc, "Integers ascending"},
{"int_desc", compare_int_desc, "Integers descending"},
{"int_abs", compare_int_abs, "Integers by absolute value"},
{"str_asc", compare_string_asc, "Strings ascending"},
{"str_desc", compare_string_desc, "Strings descending"},
{"str_len", compare_string_length, "Strings by length"}
};
int num_options = sizeof(sort_options) / sizeof(sort_options[0]);
void print_int_array(int* arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
void print_string_array(char** arr, int size) {
for (int i = 0; i < size; i++) {
printf("%s ", arr[i]);
}
printf("\n");
}
int main() {
// Test integers
int int_arr[] = {5, -3, 12, -8, 7, -1, 15};
int int_size = sizeof(int_arr) / sizeof(int_arr[0]);
// Test strings
char* str_arr[] = {"banana", "apple", "cherry", "date",
"elderberry", "fig"};
int str_size = sizeof(str_arr) / sizeof(str_arr[0]);
printf("=== Sorting with Function Pointers ===\n\n");
printf("Original integers: ");
print_int_array(int_arr, int_size);
printf("\nOriginal strings: ");
print_string_array(str_arr, str_size);
printf("\n");
// Test each sort option
for (int i = 0; i < num_options; i++) {
printf("\n--- %s: %s ---\n",
sort_options[i].name,
sort_options[i].description);
if (strncmp(sort_options[i].name, "int", 3) == 0) {
// Sort integers
int temp_arr[int_size];
memcpy(temp_arr, int_arr, sizeof(int_arr));
qsort(temp_arr, int_size, sizeof(int), sort_options[i].func);
printf("Sorted: ");
print_int_array(temp_arr, int_size);
} else {
// Sort strings
char* temp_arr[str_size];
memcpy(temp_arr, str_arr, sizeof(str_arr));
qsort(temp_arr, str_size, sizeof(char*), sort_options[i].func);
printf("Sorted: ");
print_string_array(temp_arr, str_size);
}
}
return 0;
}
2. Multi-Key Sorting
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Person structure
typedef struct {
char name[50];
int age;
double salary;
} Person;
// Comparison function types
typedef int (*PersonCompare)(const Person*, const Person*);
// Individual comparison functions
int compare_by_name(const Person* a, const Person* b) {
return strcmp(a->name, b->name);
}
int compare_by_age(const Person* a, const Person* b) {
return a->age - b->age;
}
int compare_by_salary(const Person* a, const Person* b) {
if (a->salary < b->salary) return -1;
if (a->salary > b->salary) return 1;
return 0;
}
// Composite comparison with multiple keys
int compare_by_name_then_age(const Person* a, const Person* b) {
int name_cmp = strcmp(a->name, b->name);
if (name_cmp != 0) return name_cmp;
return a->age - b->age;
}
int compare_by_age_then_salary(const Person* a, const Person* b) {
int age_cmp = a->age - b->age;
if (age_cmp != 0) return age_cmp;
if (a->salary < b->salary) return -1;
if (a->salary > b->salary) return 1;
return 0;
}
// Wrapper for qsort
int person_compare_wrapper(const void* a, const void* b, PersonCompare cmp) {
return cmp((const Person*)a, (const Person*)b);
}
// Comparison function array
PersonCompare comparators[] = {
compare_by_name,
compare_by_age,
compare_by_salary,
compare_by_name_then_age,
compare_by_age_then_salary
};
const char* comparator_names[] = {
"Name",
"Age",
"Salary",
"Name then Age",
"Age then Salary"
};
int num_comparators = sizeof(comparators) / sizeof(comparators[0]);
// Generic qsort adapter
void sort_people(Person* people, int n, PersonCompare cmp) {
// Since qsort expects a specific signature, we need a wrapper
// This is a simple bubble sort for demonstration
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (cmp(&people[j], &people[j + 1]) > 0) {
Person temp = people[j];
people[j] = people[j + 1];
people[j + 1] = temp;
}
}
}
}
void print_people(Person* people, int n) {
printf("%-20s %-5s %-10s\n", "Name", "Age", "Salary");
for (int i = 0; i < n; i++) {
printf("%-20s %-5d $%-9.2f\n",
people[i].name,
people[i].age,
people[i].salary);
}
}
int main() {
Person people[] = {
{"Alice Smith", 30, 75000.00},
{"Bob Johnson", 25, 65000.50},
{"Charlie Brown", 35, 82000.75},
{"Alice Smith", 28, 72000.00},
{"Diana Prince", 32, 90000.00},
{"Bob Johnson", 30, 68000.00}
};
int num_people = sizeof(people) / sizeof(people[0]);
printf("=== Multi-Key Sorting ===\n\n");
printf("Original list:\n");
print_people(people, num_people);
// Test each comparator
for (int i = 0; i < num_comparators; i++) {
Person temp_people[num_people];
memcpy(temp_people, people, sizeof(people));
sort_people(temp_people, num_people, comparators[i]);
printf("\nSorted by %s:\n", comparator_names[i]);
print_people(temp_people, num_people);
}
return 0;
}
Callback Arrays
1. Event Handler System
#include <stdio.h>
#include <string.h>
// Event types
typedef enum {
EVENT_MOUSE_CLICK,
EVENT_MOUSE_MOVE,
EVENT_KEY_PRESS,
EVENT_KEY_RELEASE,
EVENT_TIMER,
EVENT_NETWORK,
NUM_EVENT_TYPES
} EventType;
// Event structure
typedef struct {
EventType type;
union {
struct { int x, y; int button; } mouse;
struct { int key; int mod; } keyboard;
struct { int timer_id; } timer;
struct { int socket; char data[256]; } network;
} data;
} Event;
// Callback function type
typedef void (*EventHandler)(const Event* event);
// Event handler registry
typedef struct {
EventHandler handlers[NUM_EVENT_TYPES][10]; // Multiple handlers per event
int handler_count[NUM_EVENT_TYPES];
} EventRegistry;
// Initialize registry
void init_registry(EventRegistry* reg) {
for (int i = 0; i < NUM_EVENT_TYPES; i++) {
reg->handler_count[i] = 0;
}
}
// Register event handler
void register_handler(EventRegistry* reg, EventType type, EventHandler handler) {
if (reg->handler_count[type] < 10) {
reg->handlers[type][reg->handler_count[type]++] = handler;
printf("Registered handler for event type %d\n", type);
}
}
// Dispatch event to all registered handlers
void dispatch_event(EventRegistry* reg, const Event* event) {
printf("\nDispatching event type %d\n", event->type);
for (int i = 0; i < reg->handler_count[event->type]; i++) {
reg->handlers[event->type][i](event);
}
}
// Example handlers
void mouse_click_handler(const Event* event) {
printf(" Mouse clicked at (%d, %d) button %d\n",
event->data.mouse.x, event->data.mouse.y,
event->data.mouse.button);
}
void mouse_move_handler(const Event* event) {
printf(" Mouse moved to (%d, %d)\n",
event->data.mouse.x, event->data.mouse.y);
}
void key_press_handler(const Event* event) {
printf(" Key pressed: %c (mod: %d)\n",
event->data.keyboard.key, event->data.keyboard.mod);
}
void key_release_handler(const Event* event) {
printf(" Key released: %c\n", event->data.keyboard.key);
}
void timer_handler(const Event* event) {
printf(" Timer %d expired\n", event->data.timer.timer_id);
}
void network_handler(const Event* event) {
printf(" Network data on socket %d: %s\n",
event->data.network.socket,
event->data.network.data);
}
// Additional handlers for demonstration
void mouse_click_logger(const Event* event) {
printf(" [LOG] Mouse click recorded\n");
}
void key_press_logger(const Event* event) {
printf(" [LOG] Key press recorded\n");
}
int main() {
EventRegistry registry;
init_registry(®istry);
// Register handlers
register_handler(®istry, EVENT_MOUSE_CLICK, mouse_click_handler);
register_handler(®istry, EVENT_MOUSE_CLICK, mouse_click_logger);
register_handler(®istry, EVENT_MOUSE_MOVE, mouse_move_handler);
register_handler(®istry, EVENT_KEY_PRESS, key_press_handler);
register_handler(®istry, EVENT_KEY_PRESS, key_press_logger);
register_handler(®istry, EVENT_KEY_RELEASE, key_release_handler);
register_handler(®istry, EVENT_TIMER, timer_handler);
register_handler(®istry, EVENT_NETWORK, network_handler);
printf("=== Event Handler System ===\n");
// Simulate events
Event e1 = {EVENT_MOUSE_CLICK};
e1.data.mouse.x = 100;
e1.data.mouse.y = 200;
e1.data.mouse.button = 1;
dispatch_event(®istry, &e1);
Event e2 = {EVENT_MOUSE_MOVE};
e2.data.mouse.x = 150;
e2.data.mouse.y = 250;
dispatch_event(®istry, &e2);
Event e3 = {EVENT_KEY_PRESS};
e3.data.keyboard.key = 'A';
e3.data.keyboard.mod = 0;
dispatch_event(®istry, &e3);
Event e4 = {EVENT_KEY_RELEASE};
e4.data.keyboard.key = 'A';
dispatch_event(®istry, &e4);
Event e5 = {EVENT_TIMER};
e5.data.timer.timer_id = 1;
dispatch_event(®istry, &e5);
Event e6 = {EVENT_NETWORK};
e6.data.network.socket = 1234;
strcpy(e6.data.network.data, "Hello World");
dispatch_event(®istry, &e6);
return 0;
}
2. Menu System with Callbacks
#include <stdio.h>
#include <string.h>
// Menu item structure
typedef struct MenuItem {
char key;
const char* description;
void (*callback)(void);
struct MenuItem* submenu;
int num_subitems;
} MenuItem;
// Callback functions
void action_new_file(void) {
printf("Creating new file...\n");
}
void action_open_file(void) {
printf("Opening file...\n");
}
void action_save_file(void) {
printf("Saving file...\n");
}
void action_save_as(void) {
printf("Saving file as...\n");
}
void action_exit(void) {
printf("Exiting program...\n");
}
void action_copy(void) {
printf("Copying text...\n");
}
void action_cut(void) {
printf("Cutting text...\n");
}
void action_paste(void) {
printf("Pasting text...\n");
}
void action_undo(void) {
printf("Undoing last action...\n");
}
void action_redo(void) {
printf("Redoing last action...\n");
}
// Menu definitions
MenuItem file_menu[] = {
{'N', "New", action_new_file, NULL, 0},
{'O', "Open", action_open_file, NULL, 0},
{'S', "Save", action_save_file, NULL, 0},
{'A', "Save As", action_save_as, NULL, 0},
{'X', "Exit", action_exit, NULL, 0},
{0, NULL, NULL, NULL, 0} // Terminator
};
MenuItem edit_menu[] = {
{'U', "Undo", action_undo, NULL, 0},
{'R', "Redo", action_redo, NULL, 0},
{'C', "Copy", action_copy, NULL, 0},
{'T', "Cut", action_cut, NULL, 0},
{'P', "Paste", action_paste, NULL, 0},
{0, NULL, NULL, NULL, 0}
};
// Main menu
MenuItem main_menu[] = {
{'F', "File", NULL, file_menu, 5},
{'E', "Edit", NULL, edit_menu, 5},
{'H', "Help", action_help, NULL, 0},
{0, NULL, NULL, NULL, 0}
};
// Function to display menu
void display_menu(MenuItem* menu, const char* title) {
printf("\n=== %s ===\n", title);
for (int i = 0; menu[i].key != 0; i++) {
printf(" %c - %s\n", menu[i].key, menu[i].description);
}
printf(" Q - Back/Quit\n");
printf("Choice: ");
}
// Menu processor
void process_menu(MenuItem* menu, const char* title) {
char choice;
while (1) {
display_menu(menu, title);
scanf(" %c", &choice);
if (choice == 'Q' || choice == 'q') {
break;
}
// Find menu item
int found = 0;
for (int i = 0; menu[i].key != 0; i++) {
if (toupper(choice) == menu[i].key) {
found = 1;
if (menu[i].submenu) {
// Enter submenu
process_menu(menu[i].submenu, menu[i].description);
} else if (menu[i].callback) {
// Execute callback
menu[i].callback();
}
break;
}
}
if (!found) {
printf("Invalid choice: %c\n", choice);
}
}
}
int main() {
printf("=== Menu System with Callbacks ===\n");
process_menu(main_menu, "Main Menu");
printf("\nGoodbye!\n");
return 0;
}
Performance Benchmarks
1. Direct vs Indirect Function Calls
#include <stdio.h>
#include <time.h>
#define ITERATIONS 100000000
#define NUM_FUNCTIONS 4
// Simple test functions
int func1(int x) { return x * 2; }
int func2(int x) { return x * x; }
int func3(int x) { return x + 100; }
int func4(int x) { return x - 50; }
// Array of function pointers
int (*func_array[])(int) = {func1, func2, func3, func4};
// Direct call benchmark
void benchmark_direct() {
volatile int result = 0;
for (int i = 0; i < ITERATIONS; i++) {
result += func1(i);
result += func2(i);
result += func3(i);
result += func4(i);
}
}
// Indirect call (array index) benchmark
void benchmark_indirect() {
volatile int result = 0;
for (int i = 0; i < ITERATIONS; i++) {
result += func_array[0](i);
result += func_array[1](i);
result += func_array[2](i);
result += func_array[3](i);
}
}
// Indirect call with variable index
void benchmark_indirect_var() {
volatile int result = 0;
for (int i = 0; i < ITERATIONS; i++) {
int idx = i % 4;
result += func_array[idx](i);
}
}
double measure_time(void (*func)()) {
clock_t start = clock();
func();
clock_t end = clock();
return ((double)(end - start)) / CLOCKS_PER_SEC;
}
int main() {
printf("=== Performance Benchmark ===\n");
printf("Iterations: %d\n\n", ITERATIONS);
double direct_time = measure_time(benchmark_direct);
double indirect_time = measure_time(benchmark_indirect);
double indirect_var_time = measure_time(benchmark_indirect_var);
printf("Direct calls: %.3f seconds\n", direct_time);
printf("Indirect (array): %.3f seconds\n", indirect_time);
printf("Indirect (variable): %.3f seconds\n", indirect_var_time);
printf("\nOverhead comparison:\n");
printf(" Indirect vs Direct: %.2fx\n", indirect_time / direct_time);
printf(" Variable vs Direct: %.2fx\n", indirect_var_time / direct_time);
return 0;
}
Best Practices Summary
Do's and Don'ts
// ✅ DO: Use typedef for cleaner syntax
typedef int (*MathOp)(int, int);
// ✅ DO: Initialize arrays properly
MathOp ops[] = {add, subtract, multiply};
// ✅ DO: Check array bounds
if (index >= 0 && index < num_ops) {
result = ops[index](a, b);
}
// ✅ DO: Use const for function pointers when appropriate
const MathOp constant_ops[] = {add, subtract};
// ✅ DO: Document function pointer types
/**
* @param a First operand
* @param b Second operand
* @return Result of operation
*/
typedef int (*BinaryOp)(int, int);
// ❌ DON'T: Use incorrect syntax
int (*ops)[] = {add, subtract}; // Wrong - missing size
// ❌ DON'T: Forget bounds checking
result = ops[user_input](a, b); // Potential buffer overflow
// ❌ DON'T: Mix incompatible function types
int (*int_ops)(int, int) = {add, subtract};
float (*float_ops)(float, float) = int_ops; // Wrong!
// ❌ DON'T: Use uninitialized function pointers
MathOp* ops; // Uninitialized pointer
ops[0](a, b); // Crash!
Common Applications Summary
| Application | Use Case | Example |
|---|---|---|
| Command Processors | Menu systems, CLI | commands[choice](args) |
| State Machines | Protocol handling, games | handlers[state](event) |
| Polymorphism | Object-oriented patterns | vtable->method(obj) |
| Plugins | Extensible systems | plugins[i].process(data) |
| Sorting | Custom comparators | qsort(..., comparators[i]) |
| Event Handling | GUI, game engines | handlers[event_type][i](event) |
| Math Libraries | Function tables | math_funcs[op_code](x, y) |
Conclusion
Function pointers in arrays are a powerful feature in C that enable:
Key Benefits
- Dynamic dispatch - Choose functions at runtime
- Table-driven programming - Clean, maintainable code
- Polymorphism - Object-oriented behavior in C
- Extensibility - Easy to add new functionality
- Performance - Efficient indirect calls
- Code organization - Centralized function management
Best Practices
- Use typedef for cleaner syntax
- Always check array bounds
- Initialize arrays properly
- Document function pointer types
- Consider const-correctness
- Handle NULL function pointers
- Profile performance when needed
When to Use
- Command interpreters and menus
- State machines and protocols
- Plugin architectures
- Event-driven systems
- Mathematical function tables
- Customizable algorithms
Mastering function pointers in arrays is essential for writing flexible, extensible, and maintainable C programs.