Function pointers are one of the most powerful and often misunderstood features in C programming. They allow programs to store, pass, and invoke functions dynamically at runtime, enabling techniques like callbacks, plugin architectures, state machines, and polymorphism. For C developers, mastering function pointers opens up possibilities for writing more flexible, modular, and reusable code.
What are Function Pointers?
A function pointer is a variable that stores the address of a function rather than storing data. Just as an integer pointer stores the address of an integer variable, a function pointer stores the starting address of executable code. This allows functions to be treated as first-class citizens that can be passed as arguments, returned from other functions, and stored in data structures.
Why Function Pointers are Essential in C
- Callbacks: Enable event-driven programming and asynchronous operations.
- Polymorphism: Implement object-oriented behavior in C (virtual functions).
- Plugin Architectures: Load and use functions dynamically.
- State Machines: Store and transition between different function states.
- Generic Algorithms: Write reusable code that works with different operations.
- Dynamic Dispatch: Choose which function to call at runtime.
Basic Function Pointer Syntax
#include <stdio.h>
// Simple functions to demonstrate pointers
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 main() {
printf("=== Basic Function Pointers ===\n\n");
// Declare a function pointer that points to functions
// taking two ints and returning an int
int (*funcPtr)(int, int);
// Point to the add function
funcPtr = &add; // & is optional - function name decays to pointer
printf("add(10, 5) = %d\n", funcPtr(10, 5));
// Point to subtract
funcPtr = subtract; // Without & also works
printf("subtract(10, 5) = %d\n", funcPtr(10, 5));
// Point to multiply
funcPtr = multiply;
printf("multiply(10, 5) = %d\n", funcPtr(10, 5));
// Print function addresses
printf("\nFunction addresses:\n");
printf("add: %p\n", (void*)add);
printf("subtract: %p\n", (void*)subtract);
printf("multiply: %p\n", (void*)multiply);
return 0;
}
Function Pointer Syntax Breakdown
#include <stdio.h>
// Function prototypes
void simpleFunction(void);
int functionWithParams(int x, double y, char c);
char* stringFunction(char *str);
int main() {
printf("=== Function Pointer Syntax ===\n\n");
// Syntax: return_type (*pointer_name)(parameter_types)
// 1. Pointer to function with no parameters, void return
void (*ptr1)(void);
ptr1 = simpleFunction;
printf("ptr1 type: void (*)(void)\n");
// 2. Pointer to function with int, double, char parameters, int return
int (*ptr2)(int, double, char);
ptr2 = functionWithParams;
printf("ptr2 type: int (*)(int, double, char)\n");
// 3. Pointer to function with char* parameter, char* return
char* (*ptr3)(char*);
ptr3 = stringFunction;
printf("ptr3 type: char* (*)(char*)\n");
// 4. Array of function pointers
int (*mathOps[3])(int, int) = {add, subtract, multiply};
printf("\nArray of function pointers: int (*[3])(int, int)\n");
// 5. Function pointer as parameter
printf("\nFunction pointer as parameter type\n");
// 6. Function pointer as return type (complex!)
// This returns a pointer to a function that takes int and returns int
int (*(*getFunction(int x))(int)) {
// Implementation...
}
// Using typedef simplifies complex declarations
printf("\nUse typedef to simplify complex declarations\n");
return 0;
}
void simpleFunction(void) {
printf(" simpleFunction called\n");
}
int functionWithParams(int x, double y, char c) {
printf(" functionWithParams(%d, %.2f, %c) called\n", x, y, c);
return x;
}
char* stringFunction(char *str) {
printf(" stringFunction(\"%s\") called\n", str);
return str;
}
Using typedef with Function Pointers
#include <stdio.h>
// Define type aliases for function pointers
typedef int (*MathOperation)(int, int);
typedef void (*Callback)(const char*);
typedef int (*Comparator)(const void*, const void*);
// Functions matching the MathOperation type
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; }
// Functions matching the Callback type
void consoleLogger(const char *msg) {
printf("CONSOLE: %s\n", msg);
}
void fileLogger(const char *msg) {
printf("FILE: %s (would write to file)\n", msg);
}
void networkLogger(const char *msg) {
printf("NETWORK: %s (would send over network)\n", msg);
}
// Function that takes a MathOperation as parameter
int calculate(int x, int y, MathOperation op, const char *opName) {
printf("Performing %s: %d %s %d = ", opName, x, opName, y);
int result = op(x, y);
printf("%d\n", result);
return result;
}
// Function that returns a MathOperation based on operator
MathOperation getOperation(char op) {
switch (op) {
case '+': return add;
case '-': return subtract;
case '*': return multiply;
case '/': return divide;
default: return NULL;
}
}
int main() {
printf("=== typedef Function Pointers ===\n\n");
// Using the typedef
MathOperation op1 = add;
MathOperation op2 = subtract;
printf("10 + 5 = %d\n", op1(10, 5));
printf("10 - 5 = %d\n", op2(10, 5));
// Array of function pointers using typedef
MathOperation operations[] = {add, subtract, multiply, divide};
const char *opNames[] = {"add", "subtract", "multiply", "divide"};
printf("\nArray of operations:\n");
for (int i = 0; i < 4; i++) {
printf("20 %s 4 = %d\n", opNames[i], operations[i](20, 4));
}
// Pass function pointer as parameter
printf("\nPassing function pointers:\n");
calculate(15, 3, add, "add");
calculate(15, 3, subtract, "subtract");
calculate(15, 3, multiply, "multiply");
calculate(15, 3, divide, "divide");
// Return function pointer from function
printf("\nDynamic operation selection:\n");
char operators[] = {'+', '-', '*', '/'};
for (int i = 0; i < 4; i++) {
MathOperation op = getOperation(operators[i]);
if (op) {
printf("10 %c 3 = %d\n", operators[i], op(10, 3));
}
}
// Callback example
printf("\nCallback system:\n");
Callback logger;
logger = consoleLogger;
logger("Application started");
logger = fileLogger;
logger("Data saved");
logger = networkLogger;
logger("User logged in");
return 0;
}
Callbacks and Event Handling
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// Callback function types
typedef void (*EventHandler)(const char* event, void* userData);
typedef int (*FilterFunction)(int value);
typedef void (*SortFunction)(int* array, int size);
// Event system
typedef struct {
EventHandler handler;
void* userData;
char eventType[32];
} EventListener;
EventListener listeners[10];
int listenerCount = 0;
void registerListener(const char* eventType, EventHandler handler, void* userData) {
if (listenerCount < 10) {
strcpy(listeners[listenerCount].eventType, eventType);
listeners[listenerCount].handler = handler;
listeners[listenerCount].userData = userData;
listenerCount++;
printf("Registered listener for '%s'\n", eventType);
}
}
void triggerEvent(const char* eventType) {
printf("\nTriggering event: %s\n", eventType);
for (int i = 0; i < listenerCount; i++) {
if (strcmp(listeners[i].eventType, eventType) == 0) {
listeners[i].handler(eventType, listeners[i].userData);
}
}
}
// Example event handlers
void logHandler(const char* event, void* userData) {
printf(" LOG: Event '%s' occurred (log level: %d)\n",
event, *(int*)userData);
}
void emailHandler(const char* event, void* userData) {
printf(" EMAIL: Sending notification for '%s' to %s\n",
event, (char*)userData);
}
void smsHandler(const char* event, void* userData) {
printf(" SMS: Alert for '%s' sent to %s\n",
event, (char*)userData);
}
// Filter example
int* filterArray(int* array, int size, FilterFunction filter, int* resultSize) {
int* result = malloc(size * sizeof(int));
int count = 0;
for (int i = 0; i < size; i++) {
if (filter(array[i])) {
result[count++] = array[i];
}
}
*resultSize = count;
return result;
}
int isEven(int x) { return x % 2 == 0; }
int isOdd(int x) { return x % 2 != 0; }
int isPositive(int x) { return x > 0; }
int isGreaterThan10(int x) { return x > 10; }
void printArray(int* array, int size, const char* name) {
printf("%s: [", name);
for (int i = 0; i < size; i++) {
printf("%d", array[i]);
if (i < size - 1) printf(", ");
}
printf("]\n");
}
int main() {
printf("=== Callbacks and Event Handling ===\n\n");
// Event system demo
int logLevel = 2;
char* emailRecipient = "[email protected]";
char* phoneNumber = "+1234567890";
registerListener("startup", logHandler, &logLevel);
registerListener("startup", emailHandler, emailRecipient);
registerListener("error", logHandler, &logLevel);
registerListener("error", emailHandler, emailRecipient);
registerListener("error", smsHandler, phoneNumber);
registerListener("shutdown", logHandler, &logLevel);
triggerEvent("startup");
triggerEvent("error");
triggerEvent("shutdown");
triggerEvent("unknown");
// Filter example
printf("\n=== Filter Functions ===\n");
int numbers[] = {-5, -2, 0, 3, 7, 12, 15, 18, 21, -8};
int size = sizeof(numbers) / sizeof(numbers[0]);
printArray(numbers, size, "Original");
FilterFunction filters[] = {isEven, isOdd, isPositive, isGreaterThan10};
const char* filterNames[] = {"Even", "Odd", "Positive", ">10"};
for (int i = 0; i < 4; i++) {
int resultSize;
int* filtered = filterArray(numbers, size, filters[i], &resultSize);
printArray(filtered, resultSize, filterNames[i]);
free(filtered);
}
return 0;
}
Sorting with Custom Comparators
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Generic sorting with custom comparator
typedef int (*Comparator)(const void*, const void*);
// Person structure
typedef struct {
char name[50];
int age;
float salary;
} Person;
// Comparison functions
int compareIntAsc(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
int compareIntDesc(const void* a, const void* b) {
return *(int*)b - *(int*)a;
}
int compareStringAsc(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
int compareStringDesc(const void* a, const void* b) {
return strcmp(*(const char**)b, *(const char**)a);
}
// Person comparators
int comparePersonByName(const void* a, const void* b) {
return strcmp(((Person*)a)->name, ((Person*)b)->name);
}
int comparePersonByAge(const void* a, const void* b) {
return ((Person*)a)->age - ((Person*)b)->age;
}
int comparePersonBySalary(const void* a, const void* b) {
float diff = ((Person*)a)->salary - ((Person*)b)->salary;
return (diff > 0) - (diff < 0);
}
// Generic print function
void printIntArray(int* array, int size, const char* title) {
printf("%s: [", title);
for (int i = 0; i < size; i++) {
printf("%d", array[i]);
if (i < size - 1) printf(", ");
}
printf("]\n");
}
void printStringArray(char** array, int size, const char* title) {
printf("%s: [", title);
for (int i = 0; i < size; i++) {
printf("\"%s\"", array[i]);
if (i < size - 1) printf(", ");
}
printf("]\n");
}
void printPeople(Person* people, int count, const char* title) {
printf("\n%s:\n", title);
for (int i = 0; i < count; i++) {
printf(" %-20s age: %3d, salary: $%.2f\n",
people[i].name, people[i].age, people[i].salary);
}
}
// Generic sort function that uses comparator
void sortArray(void* array, int count, int elementSize, Comparator cmp) {
qsort(array, count, elementSize, cmp);
}
int main() {
printf("=== Custom Comparators for Sorting ===\n\n");
// Integer sorting
int numbers[] = {42, 17, 8, 99, 23, 56, 3, 71};
int numCount = sizeof(numbers) / sizeof(numbers[0]);
printIntArray(numbers, numCount, "Original");
sortArray(numbers, numCount, sizeof(int), compareIntAsc);
printIntArray(numbers, numCount, "Ascending");
sortArray(numbers, numCount, sizeof(int), compareIntDesc);
printIntArray(numbers, numCount, "Descending");
// String sorting
char* fruits[] = {"banana", "apple", "orange", "grape", "kiwi", "mango"};
int fruitCount = sizeof(fruits) / sizeof(fruits[0]);
printf("\n");
printStringArray(fruits, fruitCount, "Original");
sortArray(fruits, fruitCount, sizeof(char*), compareStringAsc);
printStringArray(fruits, fruitCount, "Alphabetical");
sortArray(fruits, fruitCount, sizeof(char*), compareStringDesc);
printStringArray(fruits, fruitCount, "Reverse alphabetical");
// Person sorting
Person people[] = {
{"Alice Johnson", 28, 65000.0},
{"Bob Smith", 35, 82000.0},
{"Charlie Brown", 22, 45000.0},
{"Diana Prince", 31, 91000.0},
{"Edward Norton", 45, 120000.0}
};
int personCount = sizeof(people) / sizeof(people[0]);
printPeople(people, personCount, "Original order");
sortArray(people, personCount, sizeof(Person), comparePersonByName);
printPeople(people, personCount, "Sorted by name");
sortArray(people, personCount, sizeof(Person), comparePersonByAge);
printPeople(people, personCount, "Sorted by age");
sortArray(people, personCount, sizeof(Person), comparePersonBySalary);
printPeople(people, personCount, "Sorted by salary");
return 0;
}
Function Tables and Dispatch
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// Command handler functions
typedef struct {
const char* command;
void (*handler)(int argc, char* argv[]);
const char* description;
} CommandEntry;
// Handler implementations
void cmd_help(int argc, char* argv[]) {
printf("Help: Available commands\n");
printf(" help - Show this help\n");
printf(" echo - Print arguments\n");
printf(" calc - Simple calculator\n");
printf(" upper - Convert to uppercase\n");
printf(" lower - Convert to lowercase\n");
printf(" exit - Exit program\n");
}
void cmd_echo(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
printf("%s", argv[i]);
if (i < argc - 1) printf(" ");
}
printf("\n");
}
void cmd_calc(int argc, char* argv[]) {
if (argc != 4) {
printf("Usage: calc <num1> <op> <num2>\n");
return;
}
double num1 = atof(argv[1]);
double num2 = atof(argv[3]);
char op = argv[2][0];
switch (op) {
case '+': printf("%.2f + %.2f = %.2f\n", num1, num2, num1 + num2); break;
case '-': printf("%.2f - %.2f = %.2f\n", num1, num2, num1 - num2); break;
case '*': printf("%.2f * %.2f = %.2f\n", num1, num2, num1 * num2); break;
case '/':
if (num2 != 0) printf("%.2f / %.2f = %.2f\n", num1, num2, num1 / num2);
else printf("Error: Division by zero\n");
break;
default: printf("Unknown operator: %c\n", op);
}
}
void cmd_upper(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
for (char *p = argv[i]; *p; p++) {
putchar(toupper(*p));
}
if (i < argc - 1) putchar(' ');
}
putchar('\n');
}
void cmd_lower(int argc, char* argv[]) {
for (int i = 1; i < argc; i++) {
for (char *p = argv[i]; *p; p++) {
putchar(tolower(*p));
}
if (i < argc - 1) putchar(' ');
}
putchar('\n');
}
void cmd_exit(int argc, char* argv[]) {
printf("Goodbye!\n");
exit(0);
}
// Command table
CommandEntry commandTable[] = {
{"help", cmd_help, "Show this help"},
{"echo", cmd_echo, "Print arguments"},
{"calc", cmd_calc, "Simple calculator"},
{"upper", cmd_upper, "Convert to uppercase"},
{"lower", cmd_lower, "Convert to lowercase"},
{"exit", cmd_exit, "Exit program"},
{NULL, NULL, NULL} // Sentinel
};
// Function to find and execute command
int executeCommand(const char* cmd, int argc, char* argv[]) {
for (int i = 0; commandTable[i].command != NULL; i++) {
if (strcmp(commandTable[i].command, cmd) == 0) {
commandTable[i].handler(argc, argv);
return 1;
}
}
return 0; // Command not found
}
void printCommandList() {
printf("Available commands:\n");
for (int i = 0; commandTable[i].command != NULL; i++) {
printf(" %-10s - %s\n", commandTable[i].command, commandTable[i].description);
}
}
int main() {
printf("=== Function Table Command Interpreter ===\n");
printf("Type 'help' for commands, 'exit' to quit\n\n");
char input[256];
char* argv[20];
while (1) {
printf("> ");
fflush(stdout);
if (!fgets(input, sizeof(input), stdin)) break;
// Remove newline
input[strcspn(input, "\n")] = 0;
// Parse command line (simple tokenization)
int argc = 0;
char *token = strtok(input, " ");
while (token != NULL && argc < 20) {
argv[argc++] = token;
token = strtok(NULL, " ");
}
if (argc == 0) continue;
// Execute command
if (!executeCommand(argv[0], argc, argv)) {
printf("Unknown command: %s\n", argv[0]);
printCommandList();
}
}
return 0;
}
State Machines with Function Pointers
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// State machine example: Traffic light controller
// Forward declarations of state functions
typedef struct TrafficLight TrafficLight;
// State function type - each state is a function
typedef void (*StateFunction)(TrafficLight*);
// Traffic light structure
struct TrafficLight {
StateFunction currentState;
int timer;
int carWaiting;
int pedestrianWaiting;
const char* stateName;
};
// State function prototypes
void redState(TrafficLight* light);
void greenState(TrafficLight* light);
void yellowState(TrafficLight* light);
void pedestrianState(TrafficLight* light);
// State implementations
void redState(TrafficLight* light) {
light->stateName = "RED";
printf("🛑 RED Light - Cars stop, pedestrians may walk\n");
// Red lasts 30 seconds
if (light->timer >= 30) {
light->currentState = greenState;
light->timer = 0;
}
}
void greenState(TrafficLight* light) {
light->stateName = "GREEN";
printf("🟢 GREEN Light - Cars go, pedestrians wait\n");
// Check for pedestrians
if (light->pedestrianWaiting && light->timer >= 10) {
light->currentState = pedestrianState;
light->timer = 0;
return;
}
// Normal green duration
if (light->timer >= 25) {
light->currentState = yellowState;
light->timer = 0;
}
}
void yellowState(TrafficLight* light) {
light->stateName = "YELLOW";
printf("🟡 YELLOW Light - Prepare to stop\n");
// Yellow lasts 5 seconds
if (light->timer >= 5) {
light->currentState = redState;
light->timer = 0;
}
}
void pedestrianState(TrafficLight* light) {
light->stateName = "PEDESTRIAN";
printf("🚶 PEDESTRIAN CROSSING - Cars wait\n");
// Pedestrian crossing lasts 15 seconds
if (light->timer >= 15) {
light->pedestrianWaiting = 0;
light->currentState = redState;
light->timer = 0;
}
}
// Initialize traffic light
void initTrafficLight(TrafficLight* light) {
light->currentState = redState;
light->timer = 0;
light->carWaiting = 0;
light->pedestrianWaiting = 0;
light->stateName = "RED";
}
// Update the traffic light (called every second)
void updateTrafficLight(TrafficLight* light) {
light->timer++;
light->currentState(light);
}
// Simulate random events
void simulateEvents(TrafficLight* light) {
// Randomly add waiting cars
if (rand() % 20 == 0) {
light->carWaiting++;
printf("🚗 Car arrived! %d cars waiting\n", light->carWaiting);
}
// Randomly add waiting pedestrians
if (rand() % 30 == 0) {
light->pedestrianWaiting = 1;
printf("🚶 Pedestrian waiting to cross\n");
}
}
int main() {
printf("=== Traffic Light State Machine ===\n\n");
srand(time(NULL));
TrafficLight light;
initTrafficLight(&light);
// Simulate 2 minutes of operation
for (int second = 1; second <= 120; second++) {
printf("\n[%d] ", second);
updateTrafficLight(&light);
simulateEvents(&light);
// Small delay for readability (not in real code)
// usleep(100000); // Uncomment for animation effect
}
printf("\n\n=== State Transition Summary ===\n");
printf("The traffic light cycled through RED → GREEN → YELLOW → RED\n");
printf("with pedestrian crossing interrupts when needed.\n");
return 0;
}
Function Pointers in Data Structures
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Generic linked list with function pointers for operations
typedef struct Node {
void* data;
struct Node* next;
} Node;
typedef struct {
Node* head;
Node* tail;
int size;
// Function pointers for operations
void (*print)(void*);
int (*compare)(void*, void*);
void (*free)(void*);
} LinkedList;
// Integer operations
void printInt(void* data) {
printf("%d", *(int*)data);
}
int compareInt(void* a, void* b) {
return *(int*)a - *(int*)b;
}
// String operations
void printString(void* data) {
printf("\"%s\"", (char*)data);
}
int compareString(void* a, void* b) {
return strcmp((char*)a, (char*)b);
}
// Person structure for more complex example
typedef struct {
char name[50];
int age;
} Person;
void printPerson(void* data) {
Person* p = (Person*)data;
printf("%s(%d)", p->name, p->age);
}
int comparePerson(void* a, void* b) {
Person* p1 = (Person*)a;
Person* p2 = (Person*)b;
return strcmp(p1->name, p2->name);
}
// List operations
LinkedList* createList(void (*print)(void*), int (*compare)(void*, void*)) {
LinkedList* list = malloc(sizeof(LinkedList));
list->head = NULL;
list->tail = NULL;
list->size = 0;
list->print = print;
list->compare = compare;
list->free = NULL;
return list;
}
void append(LinkedList* list, void* data) {
Node* newNode = malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
if (list->tail == NULL) {
list->head = list->tail = newNode;
} else {
list->tail->next = newNode;
list->tail = newNode;
}
list->size++;
}
void printList(LinkedList* list) {
printf("[");
Node* current = list->head;
while (current != NULL) {
list->print(current->data);
current = current->next;
if (current != NULL) printf(", ");
}
printf("] (size=%d)\n", list->size);
}
Node* find(LinkedList* list, void* target) {
Node* current = list->head;
while (current != NULL) {
if (list->compare(current->data, target) == 0) {
return current;
}
current = current->next;
}
return NULL;
}
void forEach(LinkedList* list, void (*operation)(void*)) {
Node* current = list->head;
while (current != NULL) {
operation(current->data);
current = current->next;
}
}
// Example operations
void doubleInt(void* data) {
*(int*)data *= 2;
}
void uppercaseString(void* data) {
char* str = (char*)data;
for (; *str; str++) {
*str = toupper(*str);
}
}
void increaseAge(void* data) {
((Person*)data)->age++;
}
int main() {
printf("=== Generic Linked List with Function Pointers ===\n\n");
// Integer list
printf("Integer List:\n");
LinkedList* intList = createList(printInt, compareInt);
int* nums = malloc(5 * sizeof(int));
for (int i = 0; i < 5; i++) {
nums[i] = i * 10;
append(intList, &nums[i]);
}
printList(intList);
// Find element
int target = 30;
Node* found = find(intList, &target);
if (found) {
printf("Found: ");
intList->print(found->data);
printf("\n");
}
// Apply operation to all elements
printf("After doubling: ");
forEach(intList, doubleInt);
printList(intList);
free(intList);
// String list
printf("\nString List:\n");
LinkedList* strList = createList(printString, compareString);
char* fruits[] = {"apple", "banana", "cherry", "date"};
for (int i = 0; i < 4; i++) {
append(strList, fruits[i]);
}
printList(strList);
printf("After uppercase: ");
forEach(strList, uppercaseString);
printList(strList);
free(strList);
// Person list
printf("\nPerson List:\n");
LinkedList* personList = createList(printPerson, comparePerson);
Person people[] = {
{"Alice", 25},
{"Bob", 30},
{"Charlie", 35}
};
for (int i = 0; i < 3; i++) {
append(personList, &people[i]);
}
printList(personList);
printf("After birthday (age+1): ");
forEach(personList, increaseAge);
printList(personList);
free(personList);
return 0;
}
Function Pointers as Return Values
#include <stdio.h>
#include <math.h>
// Function that returns a function pointer
typedef double (*MathFunc)(double);
// Basic math functions
double square(double x) { return x * x; }
double cube(double x) { return x * x * x; }
double sqrtFunc(double x) { return sqrt(x); }
double absolute(double x) { return x < 0 ? -x : x; }
double reciprocal(double x) { return x != 0 ? 1.0 / x : 0; }
// Function factory - returns appropriate function based on operation
MathFunc getFunction(char op) {
switch (op) {
case '^': return square; // Actually square, not general power
case '3': return cube;
case 'r': return sqrtFunc;
case 'a': return absolute;
case '/': return reciprocal;
default: return NULL;
}
}
// Function that composes two functions
MathFunc compose(MathFunc f, MathFunc g) {
// This is tricky - need to return a function that calls f(g(x))
// But we can't return a dynamically created function in C.
// Instead, we need a wrapper function that calls both.
// Alternative: Return a specific composed function
// For simplicity, we'll just demonstrate the concept
return NULL; // Placeholder
}
// Apply a function to an array
void applyToArray(double* array, int size, MathFunc func) {
for (int i = 0; i < size; i++) {
array[i] = func(array[i]);
}
}
void printArray(double* array, int size, const char* title) {
printf("%s: [", title);
for (int i = 0; i < size; i++) {
printf("%.2f", array[i]);
if (i < size - 1) printf(", ");
}
printf("]\n");
}
int main() {
printf("=== Function Pointers as Return Values ===\n\n");
double numbers[] = {1.0, 2.0, 3.0, 4.0, 5.0};
int size = sizeof(numbers) / sizeof(numbers[0]);
printArray(numbers, size, "Original");
// Get function based on user input
char operations[] = {'^', '3', 'r', 'a', '/'};
const char* opNames[] = {"square", "cube", "sqrt", "abs", "reciprocal"};
for (int i = 0; i < 5; i++) {
double temp[size];
for (int j = 0; j < size; j++) temp[j] = numbers[j];
MathFunc func = getFunction(operations[i]);
if (func) {
applyToArray(temp, size, func);
printf("%s: ", opNames[i]);
printArray(temp, size, "");
}
}
// Demonstration of function selection based on runtime conditions
printf("\nDynamic function selection:\n");
double x = -4.5;
char op = (x < 0) ? 'a' : '^';
MathFunc selected = getFunction(op);
printf("For x = %.2f, selected %s function: result = %.2f\n",
x, op == 'a' ? "absolute" : "square", selected(x));
return 0;
}
Advanced: Function Pointer Arrays for Efficient Dispatch
#include <stdio.h>
#include <string.h>
// Virtual method table (vtable) example - like C++ virtual functions
// Base "class"
typedef struct Animal Animal;
typedef struct {
void (*speak)(Animal*);
void (*move)(Animal*, int distance);
void (*eat)(Animal*, const char* food);
} AnimalVTable;
struct Animal {
AnimalVTable* vtable;
char name[50];
int energy;
};
// Dog implementations
void dogSpeak(Animal* self) {
printf("%s says: Woof! Woof!\n", self->name);
}
void dogMove(Animal* self, int distance) {
printf("%s runs %d meters, wagging tail\n", self->name, distance);
self->energy -= distance / 2;
}
void dogEat(Animal* self, const char* food) {
printf("%s happily eats %s\n", self->name, food);
self->energy += 10;
}
// Cat implementations
void catSpeak(Animal* self) {
printf("%s says: Meow!\n", self->name);
}
void catMove(Animal* self, int distance) {
printf("%s gracefully walks %d meters\n", self->name, distance);
self->energy -= distance / 4;
}
void catEat(Animal* self, const char* food) {
if (strcmp(food, "fish") == 0) {
printf("%s eagerly eats %s\n", self->name, food);
self->energy += 15;
} else {
printf("%s sniffs %s and walks away\n", self->name, food);
self->energy -= 1;
}
}
// Bird implementations
void birdSpeak(Animal* self) {
printf("%s says: Chirp! Chirp!\n", self->name);
}
void birdMove(Animal* self, int distance) {
printf("%s flies %d meters through the air\n", self->name, distance);
self->energy -= distance / 3;
}
void birdEat(Animal* self, const char* food) {
if (strcmp(food, "seeds") == 0 || strcmp(food, "worms") == 0) {
printf("%s pecks at %s\n", self->name, food);
self->energy += 8;
} else {
printf("%s ignores %s\n", self->name, food);
}
}
// Virtual tables
AnimalVTable dogVTable = {dogSpeak, dogMove, dogEat};
AnimalVTable catVTable = {catSpeak, catMove, catEat};
AnimalVTable birdVTable = {birdSpeak, birdMove, birdEat};
// Constructor-like functions
void createDog(Animal* animal, const char* name) {
animal->vtable = &dogVTable;
strcpy(animal->name, name);
animal->energy = 100;
}
void createCat(Animal* animal, const char* name) {
animal->vtable = &catVTable;
strcpy(animal->name, name);
animal->energy = 80;
}
void createBird(Animal* animal, const char* name) {
animal->vtable = &birdVTable;
strcpy(animal->name, name);
animal->energy = 60;
}
// Polymorphic functions
void speak(Animal* animal) {
animal->vtable->speak(animal);
}
void move(Animal* animal, int distance) {
animal->vtable->move(animal, distance);
}
void eat(Animal* animal, const char* food) {
animal->vtable->eat(animal, food);
}
void printStatus(Animal* animal) {
printf(" %s energy: %d\n", animal->name, animal->energy);
}
int main() {
printf("=== Virtual Function Tables (Polymorphism) ===\n\n");
// Create different animals
Animal dog, cat, bird;
createDog(&dog, "Buddy");
createCat(&cat, "Whiskers");
createBird(&bird, "Tweety");
Animal* animals[] = {&dog, &cat, &bird};
int animalCount = 3;
// Interact with all animals polymorphically
printf("Morning routine:\n");
for (int i = 0; i < animalCount; i++) {
speak(animals[i]);
move(animals[i], 10);
eat(animals[i], "fish");
printStatus(animals[i]);
printf("\n");
}
printf("Afternoon routine:\n");
for (int i = 0; i < animalCount; i++) {
move(animals[i], 20);
if (i == 0) eat(animals[i], "meat");
else if (i == 1) eat(animals[i], "milk");
else eat(animals[i], "seeds");
printStatus(animals[i]);
printf("\n");
}
return 0;
}
Common Pitfalls and Best Practices
#include <stdio.h>
// Pitfall 1: Incorrect function signature
void function1(int x) { printf("function1: %d\n", x); }
void function2(double x) { printf("function2: %f\n", x); }
// Pitfall 2: Returning pointer to local function (not possible in C)
// Functions are not first-class in that sense - can't create new functions at runtime
// Pitfall 3: Forgetting to check for NULL
void safeCall(void (*func)(int), int arg) {
if (func != NULL) {
func(arg);
} else {
printf("Warning: NULL function pointer\n");
}
}
// Pitfall 4: Array of function pointers with wrong signature
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
double multiply(double a, double b) { return a * b; } // Different signature
void demonstratePitfalls() {
printf("=== Common Pitfalls ===\n\n");
// Pitfall 1: Assigning to wrong function pointer type
void (*ptr1)(int) = function1; // OK
// void (*ptr2)(int) = function2; // Compiler warning - different parameter type
void (*ptr2)(double) = function2; // Correct
printf("Pitfall 1: Match signatures exactly\n");
ptr1(42);
ptr2(3.14);
// Pitfall 3: NULL check
void (*nullPtr)(int) = NULL;
safeCall(nullPtr, 10);
safeCall(ptr1, 20);
// Pitfall 4: Mixing signatures in arrays
int (*intOps[2])(int, int) = {add, subtract};
// double (*doubleOps[1])(double, double) = {multiply}; // Separate array
printf("\nPitfall 4: Keep arrays homogeneous\n");
printf("add(10,5) = %d\n", intOps[0](10, 5));
printf("subtract(10,5) = %d\n", intOps[1](10, 5));
}
// Best Practice 1: Use typedef for clarity
typedef int (*BinaryIntOp)(int, int);
// Best Practice 2: Document expected behavior
/**
* Applies a binary operation to two integers
* @param op Function pointer to binary operation
* @param a First operand
* @param b Second operand
* @return Result of operation
*/
int apply(BinaryIntOp op, int a, int b) {
return op(a, b);
}
// Best Practice 3: Use const for function pointers that shouldn't change
int execute(const BinaryIntOp op, int x, int y) {
return op(x, y);
}
// Best Practice 4: Initialize function pointers
BinaryIntOp getOperation(char op) {
switch(op) {
case '+': return add;
case '-': return subtract;
default: return NULL; // Return NULL for invalid
}
}
int main() {
demonstratePitfalls();
printf("\n=== Best Practices ===\n\n");
BinaryIntOp op = getOperation('+');
if (op) {
printf("15 + 7 = %d\n", op(15, 7));
}
op = getOperation('-');
if (op) {
printf("15 - 7 = %d\n", op(15, 7));
}
op = getOperation('*');
if (!op) {
printf("Invalid operation correctly detected\n");
}
return 0;
}
Performance Considerations
#include <stdio.h>
#include <time.h>
// Direct function call vs function pointer call
int add(int a, int b) { return a + b; }
void performanceComparison() {
const int ITERATIONS = 100000000;
int result = 0;
clock_t start, end;
// Direct function call
start = clock();
for (int i = 0; i < ITERATIONS; i++) {
result = add(result, 1);
}
end = clock();
printf("Direct call: %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
// Function pointer call
int (*funcPtr)(int, int) = add;
result = 0;
start = clock();
for (int i = 0; i < ITERATIONS; i++) {
result = funcPtr(result, 1);
}
end = clock();
printf("Function ptr: %.3f seconds\n",
(double)(end - start) / CLOCKS_PER_SEC);
printf("\nNote: Modern compilers often optimize both to same speed\n");
printf("when the function pointer is known at compile time.\n");
}
int main() {
printf("=== Performance Comparison ===\n\n");
performanceComparison();
return 0;
}
Summary Table
| Concept | Syntax | Description |
|---|---|---|
| Declare function pointer | int (*ptr)(int, int); | Pointer to function taking two ints, returning int |
| Assign | ptr = &add; or ptr = add; | Point to function |
| Call | ptr(10, 20); | Invoke function through pointer |
| Array | int (*ops[3])(int, int); | Array of function pointers |
| Parameter | void sort(void *array, Comparator cmp); | Pass function pointer |
| Return | MathFunc getFunction(char op); | Return function pointer |
| typedef | typedef int (*Op)(int, int); | Create type alias |
Conclusion
Function pointers are a powerful feature in C that enable dynamic dispatch, callbacks, polymorphism, and flexible program architecture. They transform functions from static code blocks into runtime-selectable behaviors, allowing C programs to achieve levels of flexibility typically associated with higher-level languages.
From simple callbacks to complex virtual function tables, function pointers enable:
- Runtime flexibility - Choose behaviors dynamically
- Code reuse - Generic algorithms with custom operations
- Modularity - Plugin architectures and dependency injection
- Object-oriented patterns - Polymorphism without C++
- Event-driven programming - Clean callback interfaces
While function pointers require careful attention to type signatures and NULL checking, they reward the effort with unparalleled flexibility in C programming. Mastery of function pointers marks the transition from intermediate to advanced C programming, enabling techniques that are essential for system programming, embedded systems, and performance-critical applications.