What Is a Function Pointer?
Pointers are one of the most important concepts in C/C++, and also one of the hardest to grasp. Their purpose can be roughly explained with this analogy: just as we can find a friend’s new address after they move by looking up their street number, we can use a pointer to locate and access data at a specific address in memory. We won’t dive deeper into pointers here, though — we’ll focus specifically on function pointers.
1. What Is a Function Pointer?
Data types such as int and float aren’t the only things you can work with using pointers in C/C++. Functions can also be manipulated via pointers.
When you define a function in a program, the compiler allocates a block of storage for that function’s code. The starting address of that block is called the function’s address. The function name itself represents this address. Since it’s an address, we can store it in a pointer variable. This pointer variable is called a function pointer variable, or simply a function pointer.
Its declaration looks like this:
| |
2. Why Do Function Pointers Exist?
Let’s start with a piece of code:
| |
The output is:
| |
Here I defined an Animal class with an Eat method, and three different feeding functions for different animals. The Eat method uses a function pointer to call the appropriate feeding function. When the food is grass, it calls the sheep’s feeding function; when the food is sheep, it calls the wolf’s feeding function; and so on. We can extend this to more animals, or even group them into herbivore, carnivore, and omnivore categories. Yet we only need a single class and a single method — that’s the power of function pointers.
Without function pointers, for functions with fixed return types and parameter types but differing behavior, you’d need to create a subclass for each variation to handle ever-changing requirements. But with function pointers, you simply declare a function pointer inside a method as an interface to call various pre-written functions. A single class handles the entire task — this is what’s known as abstraction and encapsulation. Function pointers turn flow-based functions into variables that can be passed as arguments, making the code far more elegant.
Another classic application of function pointers is callback functions. Consider the example above: for different foods, I used different feeding functions. Sheep, wolves, and humans are all hungry, but sheep don’t like shredded pork with green peppers, and humans don’t eat wolves. They ask you to find food, and when you find it, you call them back (callback). You find grass and call the sheep back to execute its feeding function. Now you’ve found sheep, so it’s time to call the wolf back to do something…
Those feeding functions are called callback functions, and you act as the Eat method of the Animal class. When they ask you to find food, that’s called registering a callback function. When you find the food, that’s triggering the associated event. When you call them back to eat, that’s invoking the callback function. And when they come to eat, that’s responding to the callback event.
3. How to Use Function Pointers
Refer to the code below:
| |
The output is:
| |
As you can see, the usage is no different from calling a regular function. But that doesn’t mean a function pointer is equivalent to a function name! A function name is just a symbol that gets replaced by the function’s entry address at compile time and remains fixed during execution. A function pointer, on the other hand, needs additional memory — word length — to store the function’s entry address, and its value can change during program execution.