C Function Pointers Explained: Call Functions Through Pointers
Function pointers are a powerful feature of C that allows a program to store the address of a function and call that function indirectly. They are useful for callbacks, sorting, event handling, state machines, and configurable program behavior.
What is a Function Pointer?
A function pointer is a pointer that stores the address of a function. Its type describes the function's return type and parameter types.
int add(int a, int b)
{
return a + b;
}
int (*operation)(int, int) = add;
printf("Result: %d\n", operation(10, 20));
Understanding Function Pointer Syntax
The syntax can look unusual at first. In int (*operation)(int, int), operation is a pointer to a function that accepts two int arguments and returns an int.
int (*operation)(int, int);
Calling a Function Through a Pointer
Once a function pointer points to a compatible function, the function can be called using the pointer.
int multiply(int a, int b)
{
return a * b;
}
int (*operation)(int, int) = multiply;
int result = operation(5, 6);
printf("%d\n", result);
Using the Address-of Operator
A function name can be used in a context where a pointer to that function is expected. The address-of operator can also be used explicitly.
int subtract(int a, int b)
{
return a - b;
}
int (*operation)(int, int) = &subtract;
printf("%d\n", operation(20, 8));
Function Pointers as Parameters
A function can receive another function as an argument through a function pointer. This is one of the main ways C implements callback behavior.
int add(int a, int b)
{
return a + b;
}
int calculate(int a, int b, int (*operation)(int, int))
{
return operation(a, b);
}
int result = calculate(10, 5, add);
printf("%d\n", result);
Creating a Callback
A callback is a function passed to another function so it can be invoked at an appropriate point in the program.
void process(int value, void (*callback)(int))
{
callback(value);
}
void printValue(int value)
{
printf("Value: %d\n", value);
}
process(42, printValue);
Using typedef for Function Pointers
typedef can make complex function pointer declarations easier to read and reuse.
typedef int (*Operation)(int, int);
int add(int a, int b)
{
return a + b;
}
Operation operation = add;
printf("%d\n", operation(4, 7));
Function Pointer Arrays
An array of function pointers can associate different functions with indexes or commands. This is useful for menus and dispatch tables.
void optionOne(void)
{
printf("Option One\n");
}
void optionTwo(void)
{
printf("Option Two\n");
}
void optionThree(void)
{
printf("Option Three\n");
}
void (*menu[])(void) = {
optionOne,
optionTwo,
optionThree
};
int choice = 1;
menu[choice]();
Building a Calculator with Function Pointers
A calculator can select an operation dynamically by storing different arithmetic functions in function pointers.
#include <stdio.h>
typedef double (*Operation)(double, double);
double add(double a, double b)
{
return a + b;
}
double subtract(double a, double b)
{
return a - b;
}
double multiply(double a, double b)
{
return a * b;
}
int main(void)
{
Operation operation = multiply;
printf("Result: %.2f\n", operation(6.0, 7.0));
return 0;
}
Function Pointers and qsort
The standard library qsort function accepts a comparison function through a function pointer. This allows the caller to define how elements should be ordered.
#include <stdio.h>
#include <stdlib.h>
int compareInts(const void *a, const void *b)
{
int x = *(const int *)a;
int y = *(const int *)b;
return (x > y) - (x < y);
}
int main(void)
{
int numbers[] = {40, 10, 30, 20};
size_t count = sizeof numbers / sizeof numbers[0];
qsort(numbers, count, sizeof numbers[0], compareInts);
for (size_t i = 0; i < count; i++)
{
printf("%d ", numbers[i]);
}
return 0;
}
Function Pointer vs Data Pointer
| Feature | Data Pointer | Function Pointer |
|---|---|---|
| Points To | An object or data | A function |
| Example | int *ptr | int (*func)(int) |
| Typical Use | Access memory and data | Call functions dynamically |
| Common Application | Arrays and structures | Callbacks and dispatch |
Function Pointers and Callbacks
Callbacks are useful when a library or framework needs to call application-defined behavior without knowing the exact implementation in advance.
typedef void (*Handler)(const char *message);
void handleMessage(const char *message)
{
printf("Message: %s\n", message);
}
void runTask(Handler handler)
{
handler("Task completed");
}
int main(void)
{
runTask(handleMessage);
return 0;
}
Function Pointers and State Machines
Function pointers can represent different states in a program. Each state can have its own handler function.
void idle(void)
{
printf("Idle state\n");
}
void running(void)
{
printf("Running state\n");
}
void stopped(void)
{
printf("Stopped state\n");
}
typedef void (*StateHandler)(void);
StateHandler state = running;
state();
Function Pointer Compatibility
A function pointer should point to a function with a compatible function type. The return type and parameter types are important when assigning and calling through a function pointer.
int add(int a, int b)
{
return a + b;
}
typedef int (*Operation)(int, int);
Operation operation = add;
Checking a Function Pointer
A function pointer can be compared with a null pointer constant before calling it when the pointer may be optional.
void greet(void)
{
printf("Hello!\n");
}
void (*callback)(void) = greet;
if (callback != NULL)
{
callback();
}
Common Function Pointer Mistakes
- Using an incompatible function pointer type
- Calling through a NULL function pointer
- Using an invalid function pointer
- Creating unnecessarily complicated declarations
- Forgetting the required function parameters
- Using callbacks without clearly defining their ownership and lifetime
Function Pointer Best Practices
- Use typedef for complex function pointer types
- Keep callback signatures simple
- Check optional function pointers before calling them
- Use compatible function signatures
- Document what a callback is expected to do
- Prefer clear dispatch tables over deeply nested conditional logic when appropriate
Real-World Applications
- Sorting with qsort
- Event handlers
- Callback APIs
- Command dispatch tables
- State machines
- Embedded systems
- Plugin-style architectures
Practice Exercises
- Create a function pointer for an addition function
- Build a calculator using function pointers
- Create a callback that prints processed values
- Build a menu using an array of function pointers
- Sort an array using qsort
- Create a simple state machine
- Design a small callback-based event system
Conclusion
Function pointers allow C programs to select and invoke behavior dynamically. Once you understand their syntax and type compatibility, they become especially useful for callbacks, sorting, state machines, dispatch tables, and flexible system designs.