找回密码
 立即注册
查看: 97|回复: 0

三个半例子让你大白什么是回调函数(C语言查缺-函数-回调函数)

[复制链接]
发表于 2024-7-15 18:51 | 显示全部楼层 |阅读模式
1 回调函数

在C语言中,回调函数是一种常见的编程技术,它允许我们将一个函数作为参数传递给另一个函数,并在需要时调用该函数。凡是情况下,回调函数用于实现事件措置、异步编程、状态机等功能。(如果你不清楚什么是函数指针先看第二小节。)
使用回调函数的长处

  • 代码复用:回调函数可以被多个分歧的函数调用,从而实现代码的复用。例如,在图形界面应用法式中,多个按钮可能需要执行类似的操作,我们可以将这些操作封装成一个回调函数,并将其与多个按钮相关联,从而实现代码的复用。
  • 低耦合性:回调函数可以将法式的控制流程分手成分歧的模块,减少模块之间的耦合。例如,在图形界面应用法式中,按钮的逻辑与界面的显示逻辑应该是分手的,使用回调函数可以实现这种分手,从而提高代码的可读性和可维护性。
  • 动态性:回调函数可以在运行时动态地改变,从而增强法式的可扩展性。例如,在图形界面应用法式中,我们可能需要按照用户的输入动态地改变按钮的行为,使用回调函数可以实现这种动态性。
以下4个例子,表示了代码复用、低耦合性、动态性以及时效性的特点。
在第一个例子中,可以通过更改find_element的回调函数参数,来实现分歧的功能;第二个例子表示了低耦合性与动态性;第三和第四个例子分袂在时间上表示出他们的长处。
例1: 定义了一个整数数组并调用find_element函数来查找偶数。我们将is_even函数作为回调函数传递给find_element函数,并在找到偶数时输出它的索引。
首先,我们需要定义一个函数指针类型,用于暗示回调函数的类型。这个类型应该包含回调函数的参数和返回值。例如,如果我们的回调函数需要接受一个整数参数并返回一个布尔值,我们可以定义一个函数指针类型如下:
  1. typedef bool (*callback_func)(int);
复制代码
接下来,我们可以编写一个函数,它接受一个回调函数作为参数,并在需要时调用该函数。例如,下面的示例演示了一个函数,它接受一个整数数组和一个回调函数,遍历数组并在找到满足回调函数条件的元素时遏制遍历。
  1. void find_element(int *array, int size, callback_func callback)
  2. {
  3.     for(int i = 0; i < size; i++)
  4.     {
  5.         if(callback(array[i]))
  6.         {
  7.             printf(”Found element at index %d\n”, i);
  8.             return;
  9.         }
  10.     }
  11.     printf(”Element not found\n”);
  12. }
复制代码
在这个函数中,我们使用回调函数callback来判断数组中的每个元素是否满足某个条件。如果找到了满足条件的元素,我们将输出它的索引并返回。否则,我们将输出“Element not found”。
此刻,我们可以编写一个回调函数来使用find_element函数。例如,下面的示例演示了一个回调函数,它接受一个整数并返回一个布尔值,用于判断该整数是否为偶数。
  1. bool is_even(int num)
  2. {
  3.     return num % 2 == 0;
  4. }
复制代码
最后,我们可以调用find_element函数并传递数组和回调函数作为参数。例如,下面的示例演示了如何使用find_element函数来查找偶数。
  1. int main()
  2. {
  3.     int array[] = {1, 3, 5, 6, 8, 9};
  4.     int size = sizeof(array) / sizeof(int);
  5.     find_element(array, size, is_even);
  6.     return 0;
  7. }
复制代码
<hr/>例2: 在图形界面应用法式中,当用户单击按钮时,我们需要执行一些操作,例如打开一个新窗口或在状态栏中显示一条动静。为了实现这些功能,我们可以将一个回调函数与按钮相关联,在用户单击按钮时调用该函数。
首先,我们需要定义一个回调函数,它将在按钮单击时被调用。在这个示例中,我们将回调函数定名为button_click_callback,它不接受任何参数并返回void类型。
  1. void button_click_callback()
  2. {
  3.     printf(”Button clicked!\n”);
  4. }
复制代码
接下来,我们可以定义一个按钮布局体,它包含按钮的属性和回调函数指针。在这个示例中,我们假设按钮有一个名称和一个坐标,而且可以响应单击事件。
  1. typedef struct {
  2.     char *name;
  3.     int x;
  4.     int y;
  5.     void (*click_callback)();
  6. } button_t;
复制代码
在这个布局体中,我们将click_callback定义为一个函数指针,它指向button_click_callback函数。
此刻,我们可以编写一个函数,它接受一个按钮作为参数,并在需要时调用该按钮的回调函数。下面的示例演示了一个函数,它模拟了按钮被单击的事件,并调用按钮的回调函数。
  1. void simulate_button_click(button_t *button)
  2. {
  3.     printf(”Simulating button click for button %s at (%d, %d)\n”, button->name, button->x, button->y);
  4.     button->click_callback();
  5. }
复制代码
在这个函数中,我们首先输出模拟按钮单击的动静,然后调用按钮的回调函数。
最后,我们可以创建一个按钮实例并将其与button_click_callback回调函数相关联。例如,下面的示例演示了如何创建一个名为“OK”的按钮,并在单击时调用button_click_callback函数。
  1. int main()
  2. {
  3.     button_t ok_button = {”OK”, 10, 10, button_click_callback};
  4.     simulate_button_click(&ok_button);
  5.     return 0;
  6. }
复制代码
在这个示例中,我们创建了一个名为“OK”的按钮,然后调用simulate_button_click函数来模拟按钮单击。当按钮被单击时,我们将输出“Button clicked!”的动静。
<hr/>例3:假设我们要从一个长途处事器获取数据,由于网络请求需要时间,因此我们不能在主线程中等待数据返回。一种常见的解决方式是使用异步回调函数。   
具体来说,我们可以定义一个函数,用于倡议网络请求,并将获取数据的回调函数作为参数传递给它。当网络请求完成后,该函数会将获取到的数据作为参数调用回调函数,通知调用者数据已经返回。这样,我们就可以在回调函数中措置获取到的数据,而不需要等待网络请求的返回。
以下是一个示例,展示了如何使用回调函数措置异步任务:
  1. typedef void (*data_callback)(char*);
  2. void fetchDataAsync(data_callback callback) {
  3.   // 倡议网络请求,获取数据
  4.   // ...
  5.   // 数据获取完成后,调用回调函数措置数据
  6.   char* data = ”Hello, world!”;
  7.   callback(data);
  8. }
  9. void processData(char* data) {
  10.   // 措置获取到的数据
  11.   // ...
  12. }
  13. int main() {
  14.   fetchDataAsync(processData);
  15.   // 继续执行其他任务
  16.   // ...
  17.   return 0;
  18. }
复制代码
在这个示例中,我们定义了一个fetchDataAsync函数,用于倡议网络请求,并将获取数据的回调函数作为参数传递给它。当网络请求完成后,fetchDataAsync函数会将获取到的数据作为参数调用回调函数,通知调用者数据已经返回。在main函数中,我们调用fetchDataAsync函数,并将processData函数作为回调函数传递给它。当fetchDataAsync函数获取到数据后,它会调用processData函数,措置获取到的数据。
通过使用回调函数,我们可以在异步任务完成后动态地措置获取到的数据,而不需要等待任务的返回。这种方式在措置网络请求、文件读写、按时器等异步任务时非常有用。
<hr/>例4:在单片机中断的执行过程中,回调函数函数也起到了重要的感化,为了避免中断措置法式过于复杂,我们凡是会将部门功能移至回调函数中。具体来说,我们可以在中断措置法式中调用一个回调函数,将部门需要措置的任务交给回调函数完成。这样,中断措置法式可以尽快完成对中断事件的措置,而回调函数可以在稍后的时间内完成残剩的任务,从而避免过多占用中断措置法式的时间。
<hr/>总之,回调函数是一种非常有用,它可以使法式更加灵活和可扩展。回调函数可以在法式执行期间动态地传递函数地址,并在特定条件满足时调用该函数,从而实现分歧的需求。
2 函数指针

函数指针是指向函数的指针变量。它可以像普通指针一样存储函数的地址,使得法式可以在运行时动态地调用分歧的函数。
在C语言中,函数指针的声明方式为:
  1. return_type (*pointer_name)(parameter_list);
复制代码
此中,return_type 暗示函数的返回值类型,parameter_list 暗示函数的参数列表,pointer_name 暗示指针变量的名称。例如,下面是一个函数指针的声明:
  1. int (*my_func_ptr)(int, int);
复制代码
这个函数指针的名称是 my_func_ptr,它可以指向一个返回类型为 int,参数列表为两个 int 类型的函数。
可以通过取函数的地址来初始化函数指针变量,例如:
  1. int sum(int a, int b) {
  2.     return a + b;
  3. }
  4. int (*my_func_ptr)(int, int) = ∑
复制代码
这个例子中,我们定义了一个名为 sum 的函数,它接受两个 int 类型的参数,并返回它们的和。然后,我们定义了一个函数指针 my_func_ptr,并将它初始化为指向 sum 函数的地址。此刻,我们就可以通过调用函数指针来动态地调用 sum 函数,例如:
  1. int result = (*my_func_ptr)(3, 4); // result 等于 7
复制代码
这个例子中,我们通过调用 my_func_ptr 指向的函数来计算 3 和 4 的和,得到了成果 7。注意,在调用函数指针时需要使用圆括号将其括起来,以避免优先级问题。
问题:下列两行代码有区别吗?
  1. int result = (*my_func_ptr)(3, 4); // result 等于 7  
  2. int result = my_func_ptr(3, 4); // result 等于 7
复制代码
回答:没有,两行代码的感化是不异。
在第一行代码中,我们使用了显式的间接寻址语法,即在函数指针前加上一个“*”,来指示我们要间接寻址该指针所指向的函数。然后,我们将括号括起来,并传入两个参数来调用这个函数。
在第二行代码中,我们使用了隐式的间接寻址,即直接使用函数指针变量名来调用函数。在这种情况下,编译器会自动将函数指针转换为其所指向的函数,并执行相应的操作。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-8 09:49 , Processed in 0.098242 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表