什么是函数指针

指针作为C/C++最重要的概念之一,也是最难理解的概念之一。其作用可以粗略地用这个例子解释:就像我们可以通过门牌号找到搬家后朋友的住址一样,我们可以通过指针去寻址和访问内存中指定地址的数据。不过在这里我们就不深入了,仅对函数指针作说明。


1. 什么是函数指针

不止intfloat之类的数据类型可以用指针进行操作,C/C++中,函数也可以使用指针进行操作。

如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针

其声明如下:

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

2. 为什么有函数指针

首先看一段代码:

 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;
}

其执行结果为:

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

这里我定义了一个Animal类,有一个Eat方法;然后定义了三种不同动物的进食函数Eat方法通过函数指针调用了不同的进食函数。食物是时,就调用羊的进食函数;食物是时,调用狼的进食函数;如此类推,我们可以拓展更多动物,甚至将动物按草食、肉食、杂食三个类进行封装。而我们只用了一个类,一个方法,这就是函数指针的魅力。

如果没有函数指针,对于这种确定返回值类型和参数类型,但是功能各有不同的函数。面对千变万化的需求,你需要为每种情况继承一个子类;但是有了函数指针,你只要在方法里声明一个函数指针作为一个接口去调用写好的不同函数,只用一个类便完成了这个任务,这就叫抽象封装函数指针把基于流程的函数变成了一个可以作为参数调用的变量,从而让代码变得更加优雅。

函数指针还有一个典型的应用就是回调函数,譬如上面我对于不同的食物,使用了不同的进食函数。羊、狼、人都饿了,但是羊不爱吃青椒肉丝,人也不喜欢吃狼。它们拜托你寻找食物,找到了叫回(callback)它们。你找到了,叫回了去执行进食函数;现在你找到了,那么就该叫回来去干点什么了……

此时这些进食函数就叫回调函数,而你就是Animal类的Eat方法。它们拜托你找食物就叫登记回调函数,你找到了食物叫做触发了回调关联的事件,你叫它们回来进食叫做调用回调函数,它们前来进食就叫响应回调事件

3. 函数指针怎么用

还是参见代码:

 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;

}

其执行结果为:

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

可以看到其使用方法和函数没什么区别,但这并不意味着函数指针和函数名等价!函数名只是个符号,在编译时会被替换为函数的入口地址,在执行时固定不变;而函数指针需要额外占用 字长 大小的内存空间来保存函数入口地址,同时其值在程序运行中可变。

引用