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