What are the function pointers in C?

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.

From variable pointers to function pointers

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.

From source code to machine code
1 of 5

Syntax for function pointers

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;

Code example 1

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 rectangle
int getRectangleArea(int width, int height)
{
return width * height;
}
//An example of a rectangle
const int RECTANGLE_WIDTH = 3.0;
const int RECTANGLE_HEIGHT = 5.0;
int main()
{
//Call getRectangleArea in the clasic way, without function pointers
int 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 getRectangleArea
int (*getRectangleAreaPtr)(int, int) = &getRectangleArea;
//Call the function using the function pointer
int area2 = (*getRectangleAreaPtr)(RECTANGLE_WIDTH, RECTANGLE_HEIGHT);
printf("Calling getRectangleArea(%d, %d), using function pointers, gives %d.\n", RECTANGLE_WIDTH, RECTANGLE_HEIGHT, area2);
return 0;
}

Code explanation

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.

Utility

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*)

Code example 2

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 elements
int compareInt(const void* a, const void* b)
{
const int* elem1 = (int*)a;
const int* elem2 = (int*)b;
//Shortcut for comparing the values
return *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 result
printArr(arr, "Before");
qsort(arr, NUM_ELEMENTS, sizeof(int), compareInt);
printArr(arr, "After");
return 0;
}

Code explanation

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.

Further reading

To gain more knowledge about these concepts and to master pointers and memory, visit this course for further information and practical exercises.

Copyright ©2024 Educative, Inc. All rights reserved