1. 回调函数是什么?
回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数 时,被调用的函数就是回调函数。
简而言之:把一个函数当作参数传给另一个函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
例如我们设计一个加减乘除的小程序,如图。
代码如下:
perl
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub\n");
printf(" 3:mul 4:div\n");
printf("*************************\n");
printf("请选择: ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
那现在我们要把这段代码改为含有回调函数的代码,那就需要增加一个函数,代码如下:
arduino
void calc(int (*pf)(int, int))
{
int ret = 0;
int x, y;
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
同时switch case也需要稍作修改,这里不再写出来。
2.qsort使用举例
2.1使用qsort函数排序整型数据
下面这段代码核心思想是:排序算法由库实现,而比较规则由用户通过"回调函数"提供。
c
#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
首先,int_cmp 是一个比较函数,用来告诉 qsort 两个元素谁大谁小。它的参数类型是 const void*,这是为了让 qsort 能适配任意类型的数据。在函数内部,把这两个指针强制转换成 int*,再解引用得到整数值,通过相减返回结果:正数表示第一个比第二个大,负数表示第一个比第二个小,0 表示相等。
接着在 main 函数中,调用 qsort() 进行排序,其中第一个参数是数组首地址,第二个参数是元素个数,第三个参数是每个元素的大小,第四个参数是刚刚写好的比较函数指针。qsort 在排序过程中会反复调用 int_cmp 来比较数组中的元素大小,从而完成排序。排序结束后,通过 for 循环依次打印数组内容,可以看到输出结果是从小到大的有序序列。
2.2 使用qsort排序结构数据
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu // 学生
{
char name[20];
int age;
};
// 按年龄比较
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
// 按名字比较
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void print(struct Stu s[], int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%s %d\n", s[i].name, s[i].age);
}
printf("--------\n");
}
// 按年龄排序
void test2()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
print(s, sz);
}
// 按名字排序
void test3()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
print(s, sz);
}
int main()
{
test2();
test3();
return 0;
}
首先,struct Stu 定义了一个学生结构体,每个学生有两个成员:name 表示姓名,age 表示年龄。之后我们对学生数组进行排序,其实就是对这个结构体数组排序。
cmp_stu_by_age 是一个比较函数,用来按照年龄大小比较两个学生。参数是 const void*,这是因为 qsort 要支持任意类型数据。函数内部把它们强制转换成 struct Stu*,然后访问 age 成员,相减得到结果,返回值规则是:正数表示第一个大,负数表示第二个大,0 表示相等。
3. qsort函数的模拟实现
arduino
#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size,
size);
}
}
}
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
这段代码使用冒泡排序实现,同时体现了回调函数的机制。
首先定义了一个比较函数 int_cmp,它接收两个 void* 类型的指针,将其转换为 int* 后进行解引用,得到整数值并相减,从而确定两个元素的大小关系。这里的返回值约定是正数表示第一个元素大于第二个,负数表示第一个元素小于第二个,0 表示相等。接着定义了 _swap 函数,用于交换任意类型的两个元素。通过将指针转换为 char*,按字节逐个交换。
核心的排序逻辑在 bubble 函数中实现。它接收数组首地址、元素数量、元素大小以及一个函数指针 cmp 作为参数。内部采用双重循环完成冒泡排序,每次比较相邻两个元素时,调用 cmp 函数判断顺序,如果前一个元素大于后一个,就调用 _swap 交换它们的位置。这里的 cmp 函数就是回调函数,它由使用者提供。
在 main 函数中,定义了一个整型数组,并调用 bubble 进行排序,同时传入 int_cmp 作为回调函数。排序完成后通过循环打印数组元素,结果从小到大排列。