Search

7.8 — Function Pointers

In lesson 6.7 -- Introduction to pointers, you learned that a pointer is a variable that holds the address of another variable. Function pointers are similar, except that instead of pointing to variables, they point to functions!

Consider the following function:

Identifier foo is the function’s name. But what type is the function? Functions have their own l-value function type -- in this case, a function type that returns an integer and takes no parameters. Much like variables, functions live at an assigned address in memory.

When a function is called (via the () operator), execution jumps to the address of the function being called:

At some point in your programming career (if you haven’t already), you’ll probably make a simple mistake:

Instead of calling function foo() and printing the return value, we’ve unintentionally sent function foo directly to std::cout. What happens in this case?

On the author’s machine, this printed:

0x002717f0

…but it may print some other value (e.g. 1) on your machine, depending on how your compiler decides to convert the function pointer to another type for printing. If your machine doesn’t print the function’s address, you may be able to force it to do so by converting the function to a void pointer and printing that:

Just like it is possible to declare a non-constant pointer to a normal variable, it’s also possible to declare a non-constant pointer to a function. In the rest of this lesson, we’ll examine these function pointers and their uses. Function pointers are a fairly advanced topic, and the rest of this lesson can be safely skipped or skimmed by those only looking for C++ basics.

Pointers to functions

The syntax for creating a non-const function pointer is one of the ugliest things you will ever see in C++:

In the above snippet, fcnPtr is a pointer to a function that has no parameters and returns an integer. fcnPtr can point to any function that matches this type.

The parenthesis around *fcnPtr are necessary for precedence reasons, as int *fcnPtr() would be interpreted as a forward declaration for a function named fcnPtr that takes no parameters and returns a pointer to an integer.

To make a const function pointer, the const goes after the asterisk:

If you put the const before the int, then that would indicate the function being pointed to would return a const int.

Assigning a function to a function pointer

Function pointers can be initialized with a function (and non-const function pointers can be assigned a function):

One common mistake is to do this:

This would actually assign the return value from a call to function goo() to fcnPtr, which isn’t what we want. We want fcnPtr to be assigned the address of function goo, not the return value from function goo(). So no parenthesis are needed.

Note that the type (parameters and return type) of the function pointer must match the type of the function. Here are some examples of this:

Unlike fundamental types, C++ will implicitly convert a function into a function pointer if needed (so you don’t need to use the address-of operator (&) to get the function’s address). However, it will not implicitly convert function pointers to void pointers, or vice-versa.

Calling a function using a function pointer

The other primary thing you can do with a function pointer is use it to actually call the function. There are two ways to do this. The first is via explicit dereference:

The second way is via implicit dereference:

As you can see, the implicit dereference method looks just like a normal function call -- which is what you’d expect, since normal function names are pointers to functions anyway! However, some older compilers do not support the implicit dereference method, but all modern compilers should.

One interesting note: Default parameters won’t work for functions called through function pointers. Default parameters are resolved at compile-time (that is, if you don’t supply an argument for a defaulted parameter, the compiler substitutes one in for you when the code is compiled). However, function pointers are resolved at run-time. Consequently, default parameters can not be resolved when making a function call with a function pointer. You’ll explicitly have to pass in values for any defaulted parameters in this case.

Passing functions as arguments to other functions

One of the most useful things to do with function pointers is pass a function as an argument to another function. Functions used as arguments to another function are sometimes called callback functions.

Consider a case where you are writing a function to perform a task (such as sorting an array), but you want the user to be able to define how a particular part of that task will be performed (such as whether the array is sorted in ascending or descending order). Let’s take a closer look at this problem as applied specifically to sorting, as an example that can be generalized to other similar problems.

Many comparison-based sorting algorithms work on a similar concept: the sorting algorithm iterates through a list of numbers, does comparisons on pairs of numbers, and reorders the numbers based on the results of those comparisons. Consequently, by varying the comparison, we can change the way the algorithm sorts without affecting the rest of the sorting code.

Here is our selection sort routine from a previous lesson:

Let’s replace that comparison with a function to do the comparison. Because our comparison function is going to compare two integers and return a boolean value to indicate whether the elements should be swapped, it will look something like this:

And here’s our selection sort routine using the ascending() function to do the comparison:

Now, in order to let the caller decide how the sorting will be done, instead of using our own hard-coded comparison function, we’ll allow the caller to provide their own sorting function! This is done via a function pointer.

Because the caller’s comparison function is going to compare two integers and return a boolean value, a pointer to such a function would look something like this:

So, we’ll allow the caller to pass our sort routine a pointer to their desired comparison function as the third parameter, and then we’ll use the caller’s function to do the comparison.

Here’s a full example of a selection sort that uses a function pointer parameter to do a user-defined comparison, along with an example of how to call it:

This program produces the result:

9 8 7 6 5 4 3 2 1
1 2 3 4 5 6 7 8 9

Is that cool or what? We’ve given the caller the ability to control how our selection sort does its job.

The caller can even define their own “strange” comparison functions:

The above snippet produces the following result:

2 4 6 8 1 3 5 7 9

As you can see, using a function pointer in this context provides a nice way to allow a caller to “hook” their own functionality into something you’ve previously written and tested, which helps facilitate code reuse! Previously, if you wanted to sort one array in descending order and another in ascending order, you’d need multiple versions of the sort routine. Now you can have one version that can sort any way the caller desires!

Note: If a function parameter is of a function type, it will be converted to a pointer to the function type. This means

can be equivalently written as:

This only works for function parameters, not stand-alone function pointers, and so is of somewhat limited use.

Providing default functions

If you’re going to allow the caller to pass in a function as a parameter, it can often be useful to provide some standard functions for the caller to use for their convenience. For example, in the selection sort example above, providing the ascending() and descending() function along with the selectionSort() function would make the caller’s life easier, as they wouldn’t have to rewrite ascending() or descending() every time they want to use them.

You can even set one of these as a default parameter:

In this case, as long as the user calls selectionSort normally (not through a function pointer), the comparisonFcn parameter will default to ascending.

Making function pointers prettier with typedef or type aliases

Let’s face it -- the syntax for pointers to functions is ugly. However, typedefs can be used to make pointers to functions look more like regular variables:

This defines a typedef called “validateFcn” that is a pointer to a function that takes two ints and returns a bool.

Now instead of doing this:

You can do this:

Which reads a lot nicer! However, the syntax to define the typedef itself can be difficult to remember.

In C++11, you can instead use type aliases to create aliases for function pointers types:

This reads more naturally than the equivalent typedef, since the name of the alias and the alias definition are placed on opposite sides of the equals sign.

Using a type alias is identical to using a typedef:

Using std::function in C++11

Introduced in C++11, an alternate method of defining and storing function pointers is to use std::function, which is part of the standard library <functional> header. To define a function pointer using this method, declare a std::function object like so:

As you see, both the return type and parameters go inside angled brackets, with the parameters inside parenthesis. If there are no parameters, the parentheses can be left empty. Although this reads a little more verbosely, it’s also more explicit, as it makes it clear what the return type and parameters expected are (whereas the typedef method obscures them).

Updating our earlier example with std::function:

Note that you can also type alias std::function:

Type inference for function pointers

Much like the auto keyword can be used to infer the type of normal variables, the auto keyword can also infer the type of a function pointer.

This works exactly like you’d expect, and the syntax is very clean. The downside is, of course, that all of the details about the function’s parameters types and return type are hidden, so it’s easier to make a mistake when making a call with the function, or using its return value.

Conclusion

Function pointers are useful primarily when you want to store functions in an array (or other structure), or when you need to pass a function to another function. Because the native syntax to declare function pointers is ugly and error prone, we recommend using std::function. In places where a function pointer type is only used once (e.g. a single parameter or return value), std::function can be used directly. In places where a function pointer type is used multiple times, a type alias to a std::function is a better choice (to prevent repeating yourself).

Quiz time!

1) In this quiz, we’re going to write a version of our basic calculator using function pointers.

1a) Create a short program asking the user for two integer inputs and a mathematical operation (‘+’, ‘-‘, ‘*’, ‘/’). Ensure the user enters a valid operation.

Show Solution

1b) Write functions named add(), subtract(), multiply(), and divide(). These should take two integer parameters and return an integer.

Show Solution

1c) Create a type alias named arithmeticFcn for a pointer to a function that takes two integer parameters and returns an integer. Use std::function.

Show Solution

1d) Write a function named getArithmeticFunction() that takes an operator character and returns the appropriate function as a function pointer.

Show Solution

1e) Modify your main() function to call getArithmeticFunction(). Call the return value from that function with your inputs and print the result.

Show Solution

Here’s the full program:

Show Solution


7.9 -- The stack and the heap
Index
7.7 -- Default arguments

463 comments to 7.8 — Function Pointers

  • Abhishek A Udupa

    Good morning!. My program works. Could you please check and tell me if there is anything more that could be done to improve the code. Thanks!

  • Ccir

    why i am getting 1 instead of address ! is there any way that i can get the address implicitly

  • Petar Jukić

    Hey, can you please explain me how are you using typedef here:

    http://prntscr.com/rsmswl

    (Sorry for screenshot, I think it's better than copying code and text here)

    Thanks!

  • Justin

    Is it possible to define the contents of a function in any way using a function pointer?

    • nascardriver

      No, that doesn't make sense. Can you show an example of what you're trying to do?

      • Justin

        For example:

        With the pointer to the variable, we could change the contents of the variable. If a function is in memory, isn't its contents in memory too? Since we know where the contents are, can't we change them?

        • nascardriver

          No, functions are stored in a protected memory area. You'd have to modify the raw bytes that make up the instructions of the function. They're different on each architecture. If you're willing to invoke undefined behavior, you can do this. Look into function detouring for examples.

  • Marc Stephens

    In your code above you describe one way to use std::function:

    bool validate(int x, int y, std::function<bool(int, int)> fcn);

    How I would call this?  it looks like it may be trying to define fcn?  but it is wrapped in a bool validate()???   The description does not explain how this would be used.  It moves on to use std::function in a more straight forward way.  Great site and thank you.  I'm very curious about how this works so any help is appreciated.

  • AbraxasKnister

    Am I right in believing that `auto` infering the type of the function pointer fails when overloading is present? The following fails (on cpp.sh) with "unable to deduce 'auto' from 'echo'".

  • Sam

    Here is my solution to the Quiz.

    • nascardriver

      Avoid local `static` variables. `getInteger` is not reusable, eg. if you wanted to allow the user to repeatedly calculate something, `getInteger` would keep increasing the count. You can get around this by passing the count as a parameter.

      The rest looks good :)

  • Ben Synnock

    Why not validate the integer too?

    • nascardriver

      We're not validating input in all examples because it'd make them longer and more complicated than needed. In production, you're absolutely right, input should be validated (though, without magic numbers).
      Note that if you don't use a parameter in a function, you don't need it. `validateInput` doesn't need `i`, you can remove it.

  • EJW

    With regard to this function:

    I get this compile error:

    Here is my compilation:

    • nascardriver

      There's no `default` case. If `operrator` is not handled in your `switch`-statement, the function won't return and cause undefined behavior.

  • Viktar

    Dear Alex and nascardriver,

    Could you explain is it possible to have function1 returns pointer to void function{2..5} ?

    For example,

    For some reason CodeBlocks returns : ../main.cpp|7|error: expected unqualified-id before ‘)’ token|

  • Fabio Henrique

    Hi there !
    I have a question, in line 66 of the full program:

    I successfully wrote the entire program but I used `auto` for this variable type on this line. I'm wrong to do this ?!
    I did because I thought it was pretty clear that this variable would get the return type of `getArithmeticFunction()`
    And I named the variable as `fcn_ptr` to give even more clues of it's return type.
    Let me know if I'm wrong, please.
    Thank you !

  • Mn3m

    A very simple demonstration of function pointers used to allow the user to choose the sorting type, but with bubble sort.

  • josecyc

    The func pointer definition you provide doesn't seem to be correct to compile, is there something that I'm missing?

    I'm getting the following error:

    Here's my code:

    [SOLVED]
    Edit: I found that initializing a function pointer in line is only possible in c++11, so compiling with -std=c++11 is needed, this solved it although it still left me wondering why wasn't this implemented in the original c++

    • nascardriver

      Brace initialization was added in C++11. Before that, you had to use copy or direct initialization.

      Enable and use the highest standard available (C++17), you'll run into more issues if you use old standards.

  • Mn3m

    • nascardriver

      You don't need to create a variable if you don't use it. Line 76-87 can return the functions right away.
      Always assume that a function gets called with any value that's valid for its parameter types. Although you only call `getArithmeticFunction` with the result of `getOperation`, that could change in the future or you could have a bug in `getOperation`. Adding a default case was the right choice.
      Variables should be initialized with brace initialization for higher type safety. See lesson 1.4.

  • cnoob

    Another question:
    Why does this call:

    reverse the order of the arithmetic operations? (I mean it executes y-x, instead of x-y for example.)

    (p.s.:I do not get any notification emails from this forum.)

    • nascardriver

      The order of evaluation of arguments is unspecified. If you need a specific order, call `getInteger` earlier and store the results in variables.

      Check your spam inbox. Your email address is set, you should receive notifications. If there are no notifications in spam, please point it out again.

      • cnoob

        Its very good to know, thank you! I still couldnt find email notifications though, neither in the spam inbox nor anywhere else.

        • Alex

          I checked the mail log and here's what I see (I've sanitized the IP/hostname):

          status=deferred (host fmx.somewhere.hu[1.2.3.4] said: 450 4.7.1 Spam suspect e-mail #406 [bfb743e5]

          It looks like your host is blocking emails from this site as suspected spam, probably because they use a templated format.

  • Ayushman Singh

    Is this legal:

    than of

  • Ged

    Both codes work.
    1. Wouldn't it be better to use std::function for reading purposes?
    2. Didn't you say to avoid global variables or because we are only using one file this is a viable option?

    3. When this code gets executed it starts the function from the right and division gets flipped. For example it gets y value first and only then x. So y becomes x and x becomes y. Earlier you said the program can start the function from the left or the right randomly. Is there any way to tell the program to start the function from the left or do I need to initialise 2 variables and then put them inside the function?

    • nascardriver

      1.
      I wouldn't say so. I'll reply again later.

      2.
      `arithmeticArray` shouldn't be global, it can be moved into `getArithmeticFunction`.
      This quiz might get removed entirely, I'll leave it as-is for now.

      3.
      You can store the return values in variables before passing them to `arithmeticFcn`

    • nascardriver

      I talked to Alex, the lesson has been updated. Back to your comments,

      1.
      No matter if you use regular function pointers or std::function, you should use a type alias if the type is used in multiple places, which it is.
      This quiz doesn't require the use of `std::function`, but it doesn't hurt using it :)

      2.
      The quiz has been removed, because it didn't follow good practice and didn't add challenges concerning the current chapter.

  • Ryan

    Why can't non reference parameter treated as the same as referenced parameter for function pointers?? Is it the reasons that were discussed in the passing arguments lessons.

  • Ged

    Sorry for posting a second time, but forgot to add one more question.

    1. Since you talked about typedef I remembered that there was a huge data type that I had no clue what it meant.

    Because you talked about this in the previous chapters I understand what it means. But the part "std::pair<std::string, int>" confuses me a bit. You only showed this function if we wanted to return multiple values from a function. But how does it work with an array(vector)? How to we declare it? How to take lets say the 5th index of the vector int variable. Is this even used?

    • Ged

      I just found the reply button I'm always used to it being at the bottom that didn't even notice it at the top :D

      This code will print out the whole vector. But if the user needs to input every element, how do we write that? Or lets say we want to initialise it like (int array[2] {2,4}). How should the {2,4} look like in our vector?

      • nascardriver

        `std::pair` is a type on its own, the vector doesn't really matter.

        When you add the vector, you have a list of these pairs. Each element has a string and an int.

  • Ged

    1.
    Shouldn't (size) be (size - 1) because we can't compare the last element with anything? This actually doesn't change anything because the 2nd for will be equal to size and never be used, but adds an extra cycle that we don't need.

    2. Unfortunately, type inference won’t work for function parameters (even if they have default values), so its use is somewhat limited.

    I didn't understand this sentence. You used

    And the function had a parameter so how does it not work?

    • nascardriver

      1.
      You're right. The last iteration does nothing. The inner loop won't run and the last element will be swapped with itself. Lesson updated, thanks!

      2.
      I don't understand that sentence (Or paragraph, a type alias also doesn't make return type/parameters visible) either, @Alex.

      • Alex

        re #2: was intended to be a restatement of the fact that you can't use "auto" as a function parameter type. I removed the sentence, as it's not a problem specific to function pointers.

        You're correct that type aliases hide type/parameters too. That is a shared downside of both type inference and type aliases -- that said, they're still worth using appropriately.

  • Tompouce

    Hi! I'm having a perplexing problem over here °~°
    I'm encountering an issue in my getInt() function to request an int from the user, here's the code:

    When the while loop is entered the user is prompted as normal. But when I give an input that would put cin in a fail state -like entering 'h'- cin fails, inputInt is set to 0, then it loops back as expected.
    But then, eventhough std::cin.clear() does reset the failure state of cin (I watched the fail variable in a debugger) the next std::cin >> inputInt; line does not prompt the user, and cin goes back to a failure state.
    This leads to an endless loop.
    The only thing that comes to my mind that could cause this would be if there was erroneous input left in the cin buffer, but cin.ignore() should take care of that... (maxStreamSize is a global constexpr set to numeric_limits<streamsize>::max())

    So yeah I don't get it :/ And it's not the first time I've used similar code to get user input, yet this is the first time I've had this issue.

    edit:
    I've changed the function to

    and now it works just fine, but I still don't know why the previous code didn't work...

    • nascardriver

      In your first snippet
      Line 10 fails, the stream enters a failed state.
      Line 12 doesn't do anything, because the stream is in a failed state.
      Line 8 clears the error flag, but the bad input is still in the stream.
      Line 10 fails again, repeat.

      In the second snippet, you're clearing the error flag before calling `ignore`, so that's working fine.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]