持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情
有的时候我们写技术文章,可绘画也有点类似,一个好的画家首先不会专注过多细节,而是从画面整体主题出发,大量色块来渲染画面氛围,只要氛围对了,方向正确接下来就好办了。而作为我这样门外汉往往会从细节开始,注意线条准确性,到最后细节做的比较足,反而整体效果却不是预期的。
函数指针
函数指针是指向一个存储函数的内存地址的变量,随后可以通过函数指针来调用该函数。在 js 这样函数是一等公民的语言里,可以将一些行为抛出给调用者来定义,可以通过传递函数作为参数来控制行为,将函数赋值给一边变量func = function(){return something},然后通过 func() 来直接调用这个函数。
注意函数名称为函数地址起始位置
我们都是知道函数是代码块,是一个可以完成一定功能的代码集合,通常可以理解行为,例如求和,或者添加一个按钮。有时候我们将一件复杂的事划分为若干连续动作,动作之间界限可以根据功能进行设定。
使用函数指针的场景
函数作为参数
如果要写一个排序函数,可能想让函数的调用者来选择数据的排序方式,可能是升序排序、有的时候也可能需要降序排序,以及是采用冒泡排序、快速排序还是堆排序这些都交给调用者来实现,这样就具有更大灵活性。所以可以使用函数指针,然后将
回调函数
函数指针的另一个用途是设置 "监听器 "或 "回调 "函数,当一个特定的事件发生时来调用函数。这个在 js 是非常常见的,通过回调来实现异步编程。
那么我们结合自己了解异步编程场景来说一说如何使用回调函数。例如我们监听按钮点击事件,当用户点击按钮就会触发一个事件,所以这里点击就是事件,那么发生这个事件,需要有相应处理,或者用户对网络地址发起请求来获取数据,应该网络请求是耗时的操作并且具有不确定,具体需要多长时间请求的服务器才会响应也是不确定的,所以这样需要回调机制来实现,当请求数据返回后对数据的处理。
下面代码create_button 来在屏幕上创建一个按钮,x 和 y 其位置,const char* text 表示按钮上文字,function 就是一个回调函数,可以监听用户点击事件,然后对点击按钮进行处理
void create_button(int x, int y, const char* text, function callback_func);
有些程序员可能需要以升序排序,有些人可能喜欢降序排序,还有一些人可能想要类似于但不完全是这些选择之一的东西。让你的用户指定做什么的一种方法是提供一个标志作为函数的参数,但这是不灵活的;排序函数只允许一组固定的比较类型(例如,升序和降序)。
函数指针声明与使用
声明函数指针的语法看起来比较晦涩难懂,但是随着不断接触,可能你就会对其有一定认识,其实并没有想象那么难。
void (*func)(int);
大多数人可能都困惑与上面式子。
我们简单读解一下上面 ,func 是一个指向函数的指针,接受一个类型为int(整型)的参数,函数返回一个 void。这就好像声明了一个名为 *func 的函数,接受 int 类型作为参数,并返回void 类型值。现在*func 是一个函数,那么 func 就应该是函数的指针。(这个和之前学习定义指针比较类似 int *x 声明了指向存放 int 类型内存地址,所以 *x 是一个 int 类型的数据,那么 x 就是是一个指向存放 int类型内存地址的指针)。
声明函数指针
void my_func(int a)
{
std::cout << "a: " << a << std::endl;
}
int main()
{
void (*func)(int);
func = &my_func;
return 0;
}
函数指针的调用
要调用一个函数指针所指向的函数,你要把这个函数指针当作你想调用的函数的名字。调用它的行为执行了解除引用;不需要自己去做。
(* func)(3);
请注意,函数指针的语法是比较灵活的,有时候过于灵活也不算好。在这里可以大多数指针使用方法一样,可以通过 & 来对指针取址,* 取值符号,func 和数组有点类似,本身就是一个指针,可以直接 func() 这样调用。
func(5);
函数指针应用例子
排序
让我们回到排序这个例子上,个人建议使用函数指针来写一个泛化排序程序
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void*, const void*))
注意这里使用
void*s来允许qsort对任何类型的数据进行操作(在 C++ 中,通常会使用模板来完成泛型的任务,但是 C++ 也允许使用void*指针),因为void*指针可以指向任何类型的数据内存地址。所以并不知道void*组成数组中元素的个数,这里nmemb用于提供数组中元素的个数,还需要提供base还需要提供数组的中元素的的大小。
但是我们真正感兴趣的是 qsort 的比较参数:它是一个函数指针,接收两个void *s并返回一个int。这使得任何人都可以指定如何对数组基数的元素进行排序,而不需要编写专门的排序算法。请注意,compar返回一个int;如果第一个参数小于第二个参数,指向的函数应该返回 -1,如果它们相等,则返回 0,如果第二个参数小于第一个参数则返回 1。
函数指针数组
根据状态变量取值来调用函数。
void run() { printf("start\r\n"); }
void stop() { printf("stop\r\n"); }
void exit() { printf("exit\r\n"); }
static void (*command[])(void) = {run, stop, exit};
int OnStateChange(uint state) {
if (state > 3) {
printf("Wrong state!\n");
return false;
}
command[state]();
return 0;
}
函数指针小结
语法
声明
声明函数指针,虽然可以使用 *func_pointer 来声明一个函数指针。
void (*func_pointer)(int);
调用指针函数
- 可以获取函数的地址然后
func_pointer = &func;
(* func_pointer)(a);
函数指针的作用
- 函数指针提供了一种传递如何做某事的指令的方式
- 可以编写更加灵活的函数和库,允许使用者通过传递函数指针来自定义行为
- 这种灵活性也可以通过使用带有虚拟函数的类来实现