Function Pointers in C: A Comprehensive Guide

Function Pointers in C: A Comprehensive Guide

Get a comprehensive understanding of function pointers with this in-depth guide

What are Function Pointers?

Function pointers generally are just like our normal functions that store the address of a variable. But here, what is being stored is the address of a function.

Whereas normal pointers point to data, function pointers point to functions.

So technically, a function pointer is a variable that stores the address of a function. So it means that by using this variable, we will be able to access and call the function whose memory is been stored by the function pointer.

You can check my article on pointers if you want to understand what pointers are starting from a basic level.

How to Declare Function Pointers in C

Unlike how we declare our normal pointers variable, to declare a function pointer, we will need to take note of three major things, which are:

  • The Name of the Function Pointer.

  • The Parameters of the function with their data types (eg. int, char, float etc).

  • The Return type.

  • The priority of the asterisk (*).

Every function pointer must have a name, which could be anything, but should be different from the function name, as to avoid conflicts and confusion in our code.

Next, the function pointer is supposed to carry the same number of parameters as the function it is pointing to, and not just the same number of parameters, but also the parameters should have the same data type. So we can't have a function pointer with two parameters, pointing to a function that has just one parameter, the number of parameters has to match. And also we can't have a Function Pointer with a char data type, pointing to another function with an int data type.

Next, is the return type of the function pointer, the function pointer should carry the same return type as the function it is pointing to, so if our function is declared as an int, then the function pointer that will be pointing to it should also be declared as an int function pointer.

Finally, the priority of the asterisk also matters, just as we use an asterisk and data type to declare a normal pointer, also we will be using an asterisk here too, the only difference is that the asterisks will be together with the name of the function pointer's name and they both will be enclosed inside a bracket, this is to prioritize the function as the pointer and not as a type of what it is returning. The reason for this is due to the nature of C programming, which reads things from left to right, so without prioritizing the asterisk and function name, the compiler will read the return type and then the asterisk, which will now make it just an ordinary function that returns a pointer to the data type it is been declared as.

For example, if we have these two cases:

int *ptr(int, int);

in this case, this is not a function pointer, instead, this is a function that returns a pointer to an int. Because the compiler will read the data type first, which in this case is an int and then read the asterisks, and then will interpret it as a function returning an int pointer.

To fix this issue, we are going to enclose the asterisk with the name of the function so that when the compiler reads it, it will not interpret them separately, instead, it will interpret them as one and thus interpret it as a function pointer to an int, and not as a function returning a pointer to an int.

So this is how function pointers are declared:

int (*ptr)(int, int);

How to Define Function Pointers in C

To define a function pointer, we simply use the same technique as our normal pointers, which is to declare and then equate it to the address of the function whose address it is to store, and your guess is as good as mine, to get the address of that function whose address we want to store in the function pointer, we simply make use of an ampersand (&) followed by the name of the function.

Let us look at how to do this, we can either first declare our function pointer before we now define, or we can combine both the declaration and the definition in a single line of code.

int add(int x, int y); // declaration of function

int (*ptr)(int, int); // declaration of function pointer
ptr = &add; // definition of function pointer
int add(int x, int y); // declaration of function

int (*ptr)(int, int) = &add; // declaration & definition of function pointer

These are the two ways we can define function pointers in C.

Interesting Facts About Function Pointers in C

  1. Function pointers point to code and not data. It points to the beginning of the execution of the code.

  2. Function Pointers don't need allocation and deallocation of memory. And they don't allocate and deallocate memory.

  3. A function name can also be used to get a function's address if passed directly as equal to a function pointer. (That is just the name of the function, without an ampersand).

  4. We can also have an array of function pointers.

  5. Function pointers can be used in place of switch cases.

  6. A function pointer can be passed as an argument to a function and can also be returned from a function.

The Real Use Case of Function Pointers in C

The real use case of function pointers is callbacks, now this can be done in two ways:

  • Function Pointers can be passed as arguments to other functions

  • A function that would receive a function pointer as an argument can call back the function that the pointer points to.

How to Use Function Pointers in C

Using Function pointers, we will have to define and also call the function pointer in our main function, whereas the function which is pointed to by the function pointer will be declared and defined as a stand-alone function. Let us take this example, where we will be adding two numbers together, the function will take two parameters and return their sum:

#include <stdio.h>

int add(int x,int y)
{
    return (x + y);
}

int main(void)
{
    int sum; // declares the variable to store the result
    int (*add_ptr)(int, int); // declares a function pointer
    add_ptr = &add; // defines the function pointer to the add function

    sum = add_ptr(5, 8); // call the add function using the function pointer

    printf("%d\n", sum); // print the result

    return (0);
}

Now for the function pointer definition, you mustn't make use of the ampersand for you to store the address of the function in the function pointer variable. You can just simply use the name directly, and it will store the same address.

Let us look at the same code above, but this time around, let us add subtraction, multiplication and division and then use a function pointer to point to the functions, using the function pointer to call each of those functions:

#include <stdio.h>

int add(int x,int y)
{
    return (x + y);
}

int sub(int x,int y)
{
    return (x - y);
}

int mul(int x,int y)
{
    return (x * y);
}

int div(int x,int y)
{
    return (x / y);
}

int main(void)
{
    int res1, res2, res3, res4;

    int (*add_ptr)(int, int);
    int (*sub_ptr)(int, int);
    int (*mul_ptr)(int, int);
    int (*div_ptr)(int, int);

    add_ptr = add;
    sub_ptr = sub;
    mul_ptr = mul;
    div_ptr = div;

    res1 = add_ptr(8, 5);
    res2 = sub_ptr(8, 5);
    res3 = mul_ptr(8, 5);
    res4 = div_ptr(8, 5);

    printf("%d\n", res1);
    printf("%d\n", res2);
    printf("%d\n", res3);
    printf("%d\n", res4);

    return (0);
}

In this code, we have to define a separate function pointer for each of the functions, but instead of this, if you remember, among the interesting things about function pointers, you will find out that it can be used with an array, so let us make our code more efficient by putting the function names in an array, so we can use just a single function pointer to point to any of the functions as need be with just a single function pointer instead of having numerous pointers.

#include <stdio.h>

int add(int x,int y)
{
    return (x + y);
}

int sub(int x,int y)
{
    return (x - y);
}

int mul(int x,int y)
{
    return (x * y);
}

int div(int x,int y)
{
    return (x / y);
}

int main(void)
{
    int res1, res2, res3, res4;
    int (*fptr[4])(int, int) = { add, sub, mul, div };

    res1 = fptr[0](8, 5);
    res2 = fptr[1](8, 5);
    res3 = fptr[2](8, 5);
    res4 = fptr[3](8, 5);

    printf("%d\n%d\n%d\n%d\n", res1, res2, res3, res4);

    return (0);
}

Now we have this code that is more efficient than the previous one. Here we defined an array of the function pointer for the 4 functions. Thus, the function pointer is declared as an array of 4 elements as that is the size of what we want to store. That is the 4 functions that are to be pointed to by the function pointer.

And to access each of the functions using the function pointers, we can simply just use the index to access them, followed by the arguments.

We can even still further reduce our code by using a loop to access each of the functions and prints the answer.

#include <stdio.h>

int add(int x,int y)
{
    return (x + y);
}

int sub(int x,int y)
{
    return (x - y);
}

int mul(int x,int y)
{
    return (x * y);
}

int div(int x,int y)
{
    return (x / y);
}

int main(void)
{
    int res;
    int i = 0;
    int (*fptr[4])(int, int) = { add, sub, mul, div };

    while (i < 4)
    {
        res = fptr[i](8, 5);
        printf("%d\n", res);
        i++;
    }

    return (0);
}

Conclusion

We will be looking at more advanced concepts using function pointers, like using function pointers as the return type for other functions and using function pointers as parameters as well as using it with structs (structures) and typedef (type definition) in the next article on function pointers.

Thank you for reading. You can connect with me on Twitter and LinkedIn.