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:

1
2
//函数返回值类型 (* 指针变量名) (函数参数列表);
int (*function_ptr_name) (int);

2. Why Do Function Pointers Exist?

Let’s start with a piece of code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

class Animal {
public:
    void Eat(string food, void(*ClassificationOfAnimals)(string)) {
        ClassificationOfAnimals(food);
        cout << "非常美味!" << endl;
    }
};

//草食动物->羊
void Sheep(string food) {
    cout << "羊吃" << food << endl;
}

//肉食动物->狼
void Wolf(string food) {
    cout << "狼吃" << food << endl;
}

//杂食动物->人
void Human(string food) {
    cout << "人吃" << food << endl;
}

int main(void) {
    Animal animal;
    animal.Eat("草", Sheep);
    animal.Eat("羊", Wolf);
    animal.Eat("青椒肉丝", Human);
    cout << "大家都吃饱了!" << endl;
}

The output is:

1
2
3
4
5
6
7
羊吃草
非常美味!
狼吃羊
非常美味!
人吃青椒肉丝
非常美味!
大家都吃饱了!

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

//返回二者最大值
int Max(int x, int y) {
    return x > y ? x : y;
}

int main(void) {
    //指针function_ptr被初始化为指向函数Max所在的地址
    int (*function_ptr)(int, int) = Max;

    int a = 22, b = 33, c = 44;
    cout << "a、b中的最大值为:" << function_ptr(a, b) << endl;
    cout << "a、b、c中的最大值为:" << function_ptr(function_ptr(a, b), c) << endl;

}

The output is:

1
2
a、b中的最大值为:33
a、b、c中的最大值为:44

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.

References