Chapter 7

Pointers to Functions

The pointers we have looked at so far have all been pointers to various types of data objects, (such as int, char, double, struct person, and pointer-to-int), but it is also possible to have pointers to functions. Pointers to functions are useful for approximately the same reasons as pointers to data: when you want an extra level of indirection, or when you’d like the same piece of code to call different functions depending on circumstances.

Subsections of Pointers to Functions

Function Pointer Basics

Declaration Syntax

Here is the syntax for declaring a function pointer:

return_type (*ptr_name) (args);

This declares ptr_name as a pointer to a function that returns something of type return_type and that takes the argument types described in args. Here, args is a comma-separated lists of the argument types for a function. For example, args would be (int, double) for a function that took two arguments – an int followed by a double.

The extra parentheses around (*ptr_name) are needed because there are precedence relationships in declarations just as there are in expressions. If instead we did:

//THIS IS INCORRECT FOR DECLARING A FUNCTION POINTER!
return_type *ptr_name (args);

We would be declaring a function (NOT a function pointer) whose return type was return_type*.

Declaration Example

Suppose we wish to declare a pointer to a function that returns an int and takes two parameters – an int followed by a char*. We would write:

int (*fn_ptr) (int, char*);

Now fn_ptr is a variable of type function pointer to a function that returns an int and takes (int, char*) as arguments.

Initialization Syntax

To initialize our fn_ptr variable, we need an existing function whose header matches its declaration – one that returns an int and takes (int, char*) as arguments. Suppose we have the following function:

int addToLength(int num, char* str) {
    return num + strlen(str);
}

Suppose we also have our fn_ptr variable declared in a main method. Now we can initialize fn_ptr to point to the beginning of the executable code in the addToLength function.

int main() {
    char* test = "hello";       //declares string constant "hello"

    int (*fn_ptr)(int, char*);  //declares our function pointer, fn_ptr

    fn_ptr = addToLength;       //now fn_ptr points to the addToLength function

    return 0;
}

Note that when we do:

fn_ptr = addToLength;

That we mean: assign to fn_ptr the memory address of the beginning of the executable code in the addToLength function. We could have more pedantically written:

fn_ptr = &addToLength;

To explicitly get the memory address of the addToLength function, and assign that to fn_ptr. However, the & is optional in this case since there is no other way to interpret assigning to a function pointer variable.

Using Function Pointers

After you have declared and initialized a function pointer variable, you can use it to call the referenced function (passing the necessary arguments). In our example, we could modify our main method to look like:

int main() {
    int result;                 //declare result variable

    char* test = "hello";       //declares string constant "hello"

    int (*fn_ptr)(int, char*);  //declares our function pointer, fn_ptr

    fn_ptr = addToLength;       //now fn_ptr points to the addToLength function

    int ans = fn_ptr(3, test);  //call the function referenced by fn_ptr,
                                //passing 3 and test. Store the returned value
                                //in the ans variable

    printf("%d\n", ans);     //in our example, prints 8
                                //(the length of "hello" plus 3)

    return 0;
}

Note that we use fn_ptr as follows:

int ans = fn_ptr(3, test);

This calls the function referenced by fn_ptr (the addToLength function), passing the arguments 3 and test. The value returned by the referenced (addToLength) function is stored in the ans variable.

The fn_ptr variable holds the memory address of the beginning of the executable code for the addToLength function. When we do int ans = fn_ptr(3, test), we want to go to the memory location stored in fn_ptr, and begin executing that code. In other words, we wish to dereference fn_ptr, which we can do with the * operator. We could more precisely write:

int ans = (*fn_ptr)(3, test);

To first go to the executable code referenced by fn_ptr, and then to start executing that code with arguments 3 and test. As with using the & operator when initializing function pointers, using the dereferencing operator when using a function pointer is optional. There is no other possible meaning of fn_ptr(3, test), so the function pointer is dereferenced whether we explicitly use the operator or not.

If we do choose to explicitly dereference, we need to be careful with order of operations. If we instead did:

//THIS IS INCORRECT FOR USING A FUNCTION POINTER!
int ans = *fn_ptr(3, test);

Then we would first go to the executable code referenced by fn_ptr (the addToLength function), passing our arguments 3 and test. That code would return 8, which is the sum of and the argument 3 and the string length of hello. Finally, we would assign to ans the result of dereferencing the 8. This will lead to either a segmentation fault (the program crashing) or to grabbing an arbirtrary value in memory and claiming it as our answer.

Functions and Function Pointers

Since a function pointer is a valid type, we can pass function pointers as arguments to functions and can also return them from functions.

Passing Function Pointers to Functions

Recall that the syntax for declaring a function ponter is:

return_type (*ptr_name) (args);

Where ptr_name is the new variable name for a function pointer that returns something of type return_type and that takes the argument types described in args. We can similarly accept a function pointer as an argument to a function using the same syntax.

For example, consider the following program:

#include <stdio.h>

int plus(int a, int b) {
    return a + b;
}

int minus(int a, int b) {
    return a - b;
}

int times(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    return a / b;
}

//op is a function pointer to a function that takes two integer arguments
//and returns an integer result
int doOperation(int (*op)(int, int), int x, int y) {

    //calls the function pointed to by op and returns the result
    return op(x, y);
}

int main() {
    int num1 = 3;
    int num2 = 4;

    printf("Added result: %d\n", doOperation(plus, num1, num2));
    printf("Multiplied result: %d\n", doOperation(times, num1, num2));

    return 0;
}

The code above will print:

Added result: 7
Multiplied result: 12

The above example might seem unnecessarily complicated, as we could have directly called plus and times from main and bypassed the doOperation function altogether. However, using function pointers as arguments can be very powerful – for example, the stdlib library defines a qsort function that accepts a comparator function pointer as an argument. This way, we can use the same sorting function to sort in a variety of ways – ascending order, descending order, by length, etc. – by passing in a different comparator function pointer.

Typedef and Function Pointers

Listing the type of function pointers can be tedious and error-prone. It can be much easier to use typedef once to create a new (more simply named) type representing a particular kind of function pointer, and then use the new type name after that. For example, in our math operations program above, we can first create a new type name for our function pointer type:

typedef int (*function) (int, int);

This creates a new type called function that represents a function pointer to a function that returns an int and takes two int arguments. We can then use the type function in our doOperation method instead of writing out the argument int (*op)(int, int). Here is the new doOperation function:

int doOperation(function op, int x, int y) {

    //calls the function pointed to by op and returns the result
    return op(x, y);
}

Note that the type name function can be anything – this is just the new type name we happened to pick when using typedef.

Returning Function Pointers from Functions

Returning function pointers from functions is exactly the same idea as returning any other other type from a function. Ideally, we would first use typedef to create a new type name for the desired type of function pointer. For example, we could add a new function to our operations example above that returns a pointer to the correct operation function based on a char argument:

function getOperation(char c) {
    if (c == '+') {
        return plus;
    }
    else if (c == '-') {
        return minus;
    }
    else if (c == '*') {
        return times;
    }
    else {
        //will return 'divide' if a non-operation char argument
        //is passed

        return divide;
    }
}

Then, we could use our getOperation function to get the correct operation based on user input:

char op;
int num1, num2;

printf("Enter number op number (no spaces, like 3+2): ");
scanf("%d%c%d", &num1, &op, &num2);

function opResult = getOperation(op);
printf("Result: %d\n", opResult(num1, num2));

By saving the correct operation function in opResult, we could then call opResult (which would call either plus, minus, times, or divide based on the value of op) to get the result of the operation.

Arrays of Function Pointers

We can declare arrays of function pointers using the same syntax we use to create arrays of any other type. As we saw in the previous section, it is often easier to first rename our function pointer using typedef, and then to use that new type when declaring an array.

Consider our operations functions and newly created function type from the previous section:

//'function' is the name of a new function pointer type
//describing a function that returns an int and takes two int arguments
typedef int (*function) (int, int);

int plus(int a, int b) {
    return a + b;
}

int minus(int a, int b) {
    return a - b;
}

int times(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    return a / b;
}

Since each operation function is the same type, we can create an array of these four function pointers. We can then ask the user to enter an array index corresponding to the desired operation, and access the appropriate function pointer to execute the operation:

int main() {
    int num1 = 3;
    int num2 = 4;

    int choice;

    //declares an array of four function pointers
    function allOps[4] = {plus, minus, times, divide};

    printf("Enter 0 for plus, 1 for minus, 2 for times, and 3 for divide: ");
    scanf("%d", &choice);

    printf("Result: %d\n", (allOps[choice])(num1, num2));

    return 0;
}

Here, allOps[choice] accesses a particular function pointer by array index, and then calls that function (passing num1 and num2). For example, if the user entered 2, then allOps[choice] would access the times function. The code would print:

Result: 12

Since times(num1, num2) would return 12.