What is Function Pointer in C ?
A function pointer in C is a variable that stores the address of a function. It allows you to treat functions as data and pass them as arguments to other functions or assign them to variables. Function pointers provide a way to achieve dynamic or runtime polymorphism in C.
The syntax of a function pointer declaration in C is as follows:
return_type (*pointer_name)(parameter_list);
Function pointers are used in C for various purposes, including:
- Callback functions: Function pointers can be passed as arguments to other functions, allowing those functions to call the specified function at a later point in the program execution. This is often used for event handling or implementing customizable behavior.
- Implementing data structures and algorithms: Function pointers can be used to create data structures such as function tables or arrays of functions. This allows different functions to be selected and executed dynamically based on specific conditions or inputs.
To declare a function pointer, you need to specify the function signature it points to.Here’s an example of declaring and calling a function pointer:
#include <stdio.h>
void sayHello() {
printf("Hello, World!\n");
}
int main() {
void (*ptr)() = sayHello; // Declare function pointer and assign the address of sayHello
ptr(); // Call the function using the pointer
return 0;
}
Output:
Hello, World!
Explanation:
In above example, we declare a function sayHello
that prints “Hello, World!” when called. Then, in the main
function, we declare a function pointer ptr
using the syntax void (*ptr)()
. We assign the address of the sayHello
function to ptr
using the assignment operator =
. Finally, we call the function using the function pointer ptr()
.
Different ways to use Function Pointer in C with examples!
1. Callback Functions
Function pointers are often used as callback functions, allowing you to specify custom behavior that will be called at a specific point in your program. This is commonly used in event handling systems or libraries where the exact behavior needs to be customizable. By passing a function pointer as an argument to another function, you can provide a callback function that gets executed when certain conditions are met.
Example: Implementing a callback function for event handling.
#include <stdio.h>
void eventHandler(void (*callback)()) {
printf("Event occurred!\n");
callback();
}
void callbackFunction() {
printf("Callback function called!\n");
}
int main() {
eventHandler(callbackFunction);
return 0;
}
Output:
Event occurred!
Callback function called!
Explanation:
In above example, we have an eventHandler
function that takes a function pointer callback
as an argument. When an event occurs, the eventHandler
function is called and it executes the callback function provided as an argument. In the main
function, we pass callbackFunction
as the callback to eventHandler
. When the event occurs, the callback function is called and its corresponding message is printed.
2. Function Tables
Function pointers can be used to create arrays or tables of functions. This technique is often used when you have multiple functions with similar behavior, and you want to select and execute the appropriate function dynamically. By storing the function pointers in an array or a table, you can choose the desired function based on specific conditions or user input.
Example: Using a function table to perform different operations on integers.
#include <stdio.h>
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 a / b;
else
return 0;
}
typedef int (*MathOperation)(int, int);
int main() {
MathOperation operations[] = {add, subtract, multiply, divide};
int choice, a, b;
printf("Enter two numbers: ");
scanf("%d %d", &a, &b);
printf("Enter the operation (0:add, 1:subtract, 2:multiply, 3:divide): ");
scanf("%d", &choice);
int result = operations[choice](a, b);
printf("Result: %d\n", result);
return 0;
}
Output:
Enter two numbers: 10 5
Enter the operation (0:add, 1:subtract, 2:multiply, 3:divide): 2
Result: 50
Explanation:
In above example, we define four mathematical operations: add
, subtract
, multiply
, and divide
. We create a function table operations
using a typedef MathOperation
, which is an array of function pointers. The user selects an operation using an integer choice. Based on the choice, the corresponding function from the operations
array is called with the provided numbers a
and b
, and the result is displayed.
3. Dynamic Function Dispatch
Function pointers allow you to implement runtime polymorphism, where the behavior of a program can be determined at runtime based on the actual type or conditions. By using function pointers, you can switch between different functions dynamically, enabling flexible and extensible program behavior.
Example: Implementing dynamic function dispatch using a function pointer.
#include <stdio.h>
typedef void (*DynamicFunction)();
void functionA() {
printf("This is function A\n");
}
void functionB() {
printf("This is function B\n");
}
void functionC() {
printf("This is function C\n");
}
int main() {
DynamicFunction func;
int choice;
printf("Enter the function number (1:A, 2:B, 3:C): ");
scanf("%d", &choice);
switch (choice) {
case 1:
func = functionA;
break;
case 2:
func = functionB;
break;
case 3:
func = functionC;
break;
default:
printf("Invalid choice!\n");
return 1;
}
func(); // Dynamic function dispatch
return 0;
}
Output:
Enter the function number (1:A, 2:B, 3:C): 2
This is function B
Explanation:
In above example, we have three functions: functionA
, functionB
, and functionC
. We define a typedef DynamicFunction
for a function pointer type. The user selects a function by entering a number, and based on the choice, the corresponding function is assigned to the func
function pointer. Finally, the function is called using func()
, resulting in the execution of the selected function.
4. Sorting and Comparisons
Function pointers are useful in sorting algorithms where the comparison criteria need to be customizable. By passing a function pointer that defines the comparison logic, you can sort data based on different criteria without modifying the sorting algorithm itself. This approach allows for greater code reuse and flexibility.
Example: Sorting an array using a function pointer for comparison.
#include <stdio.h>
int ascendingOrder(int a, int b) {
return a - b;
}
int descendingOrder(int a, int b) {
return b - a;
}
void bubbleSort(int arr[], int size, int (*compare)(int, int)) {
int i, j, temp;
for (i = 0; i < size - 1; i++) {
for (j = 0; j < size - i - 1; j++) {
if (compare(arr[j], arr[j + 1]) > 0) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int numbers[] = {5, 2, 8, 1, 9};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Original array: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
bubbleSort(numbers, size, ascendingOrder);
printf("Ascending order: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
bubbleSort(numbers, size, descendingOrder);
printf("Descending order: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
Output:
Original array: 5 2 8 1 9
Ascending order: 1 2 5 8 9
Descending order: 9 8 5 2 1
Explanation:
In above example, we have two comparison functions, ascendingOrder
and descendingOrder
, which define the sorting order. The bubbleSort
function takes an array, its size, and a function pointer compare
as arguments. The function pointer compare
is used to determine the order of comparison in the sorting algorithm. By passing different comparison functions to bubbleSort
, we can achieve different sorting orders. The array numbers
is sorted first in ascending order and then in descending order.
5. Function Wrappers
Function pointers can be used to wrap functions with additional behavior or to provide hooks for pre- and post-processing. By wrapping functions with function pointers, you can add logging, error handling, performance profiling, or other functionality without modifying the original function implementation.
Example: Wrapping a function with additional behavior using function pointers.
#include <stdio.h>
void originalFunction() {
printf("This is the original function.\n");
}
void wrapperFunction(void (*original)()) {
printf("Before calling the original function.\n");
original();
printf("After calling the original function.\n");
}
int main() {
wrapperFunction(originalFunction);
return 0;
}
Output:
Before calling the original function.
This is the original function.
After calling the original function.
Explanation:
In above example, we have an originalFunction
that performs a specific task. The wrapperFunction
takes a function pointer original
as an argument, wraps additional behavior before and after calling the original function, and then calls the original function. In the main
function, we pass originalFunction
as the argument to wrapperFunction
, resulting in the execution of both the wrapper and the original function.
6. State Machines
Function pointers can be used to implement state machines where each state corresponds to a specific function. By assigning the appropriate function pointer based on the current state, you can define the behavior of the state machine dynamically.
Example: Implementing a simple state machine using function pointers.
#include <stdio.h>
typedef void (*StateFunction)();
void stateOne() {
printf("State One\n");
}
void stateTwo() {
printf("State Two\n");
}
void stateThree() {
printf("State Three\n");
}
int main() {
StateFunction currentState = stateOne;
// Transition from stateOne to stateTwo
printf("Transition from stateOne to stateTwo:\n");
currentState = stateTwo;
currentState();
// Transition from stateTwo to stateThree
printf("Transition from stateTwo to stateThree:\n");
currentState = stateThree;
currentState();
return 0;
}
Output:
Transition from stateOne to stateTwo:
State Two
Transition from stateTwo to stateThree:
State Three
Explanation:
In above example, we have three state functions: stateOne
, stateTwo
, and stateThree
. We define a typedef StateFunction
for a function pointer type. The currentState
function pointer represents the current state of the state machine. By assigning different state functions to currentState
, we simulate transitions between states. In this example, we transition from stateOne
to stateTwo
and then from stateTwo
to stateThree
.
7. Dynamic Library Loading
Function pointers are essential when working with dynamically loaded libraries (DLLs) or shared objects (SOs) in C. Function pointers are used to bind and invoke functions within the loaded library, allowing your program to use functions that are not linked at compile time.
Example: Loading a dynamic library and using function pointers to call its functions.
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef int (*AddFunction)(int, int);
int main() {
void* handle = dlopen("./libmath.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Failed to load the library: %s\n", dlerror());
return 1;
}
AddFunction add = (AddFunction)dlsym(handle, "add");
if (!add) {
fprintf(stderr, "Failed to find the symbol: %s\n", dlerror());
dlclose(handle);
return 1;
}
int result = add(10, 5);
printf("Result: %d\n", result);
dlclose(handle);
return 0;
}
Output:
Result: 15
Explanation:
In above example, we load a dynamic library named libmath.so
using the dlopen
function from the dlfcn.h
header. We obtain a handle to the loaded library, and then we use dlsym
to retrieve the function pointer to the add
function defined in the library. We cast the function pointer to the correct function type, and finally, we call the add
function through the function pointer. The result is displayed, and the library is closed using dlclose
.