Function pointers are an important concept, although they can be challenging to grasp. Most developers are more familiar with pointers to variables because they are more concrete and easier to understand.
Pointers hold the value of a variable, which is easy to accept because a variable has a location in memory. Similarly, function pointers hold a function’s address in memory, where the function’s executable code is stored.
While compiling a source file, the compiler translates the C code into machine code consisting of instructions for the CPU. These instructions are simply the data stored in memory along with the values of variables. The executable file contains a special section called .text
or .code
, which holds the instructions for the functions. When the program runs, the executable file and its associated data are loaded into memory.
We can create a function pointer by using the address-of operator (&
) to retrieve the address of a function and then initializing a pointer to the function using the following syntax:
return_type(*pointer_name)(args_types)
The ()
around the pointer name is required to distinguish between function declarations and function pointer declarations. Without them, the declaration becomes return_type *pointer_name (args_types)
and reads as a function named pointer_name
, which accepts args_types
and returns a pointer to a variable of type return_type
.
For example:
float dummy(int x, char y)
{
//implementation does not matter
}
We use &dummy
to retrieve the address of the function and write the pointer:
float(*functionPtr)(int, char) = &dummy;
Here’s an example that uses a function pointer to call the getRectangleArea
function, which calculates the area of a rectangle given its height and width:
#include <stdio.h>//Computes the area of a rectangleint getRectangleArea(int width, int height){return width * height;}//An example of a rectangleconst int RECTANGLE_WIDTH = 3.0;const int RECTANGLE_HEIGHT = 5.0;int main(){//Call getRectangleArea in the clasic way, without function pointersint area1 = getRectangleArea(RECTANGLE_WIDTH, RECTANGLE_HEIGHT);printf("Calling getRectangleArea(%d, %d) gives %d.\n", RECTANGLE_WIDTH, RECTANGLE_HEIGHT, area1);//Create a function pointer that points to getRectangleAreaint (*getRectangleAreaPtr)(int, int) = &getRectangleArea;//Call the function using the function pointerint area2 = (*getRectangleAreaPtr)(RECTANGLE_WIDTH, RECTANGLE_HEIGHT);printf("Calling getRectangleArea(%d, %d), using function pointers, gives %d.\n", RECTANGLE_WIDTH, RECTANGLE_HEIGHT, area2);return 0;}
In the example code,
Lines 4–7: We create a function to compute the area of a rectangle.
Lines 10–11: We define a rectangle for testing.
Line 16: We call the function using its name and arguments.
Line 20: We create a function pointer called getRectangleAreaPtr
, which points to a function that accepts two int
arguments and returns an int
.
Line 23: We call the function using the function pointer by dereferencing the pointer (*
) and providing the arguments, obtaining the same result.
Function pointers allow passing functions as arguments to other functions, which helps customize the behavior of a function while keeping most of the code the same.
For example, a sorting algorithm may accept a function pointer as an argument to tell how to compare a specific data type. The qsort
function from the standard library is a great example.
The prototype is as follows:
void qsort(void *base, size_t nitems, size_t size, int (*compare)(const void *, const void*))
The function pointer argument points to a function that accepts two const void
pointers arguments and returns an int
value.
int (*compare)(const void *, const void*)
Consider the following example. We have hidden the printArr
function implementation because it’s not relevant to our example.
//Comparator function to decide the order in which qsort will sort the elementsint compareInt(const void* a, const void* b){const int* elem1 = (int*)a;const int* elem2 = (int*)b;//Shortcut for comparing the valuesreturn *elem1 - *elem2;}int main(){int arr[NUM_ELEMENTS] = {3, -1, 5, 4, 100, 9, 22, 33, -100, 0};//Print the initial array, sort, print the resultprintArr(arr, "Before");qsort(arr, NUM_ELEMENTS, sizeof(int), compareInt);printArr(arr, "After");return 0;}
The example code sorts the array in ascending order:
Lines 2–9: We define the comparator function, which accepts void
pointers to two elements, converts them to int
, and compares the values by subtracting them.
Line 13: We create an unsorted array.
Line 17: We call qsort
, specifying the starting address, the number of elements, the size of each element, and a pointer to a comparator function compareInt
.
To gain more knowledge about these concepts and to master pointers and memory, visit this course for further information and practical exercises.