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.
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/
