文章目录
- 0、总结
- 1、回调函数是什么?
- 2、改善代码
-
- [2.1 使用回调函数改造前](#2.1 使用回调函数改造前)
- [2.2 使用回调函数改造后](#2.2 使用回调函数改造后)
- 3、qsort使用举例
-
- [3.1 使用qsort函数排序整型数据](#3.1 使用qsort函数排序整型数据)
- [3.2 使用qsort排序结构数据](#3.2 使用qsort排序结构数据)
- 4、qsort函数的模拟实现
0、总结

1、回调函数是什么?
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
简单画流程图理解:

- 主函数将回调函数指针作为参数传递给调用函数(注册回调)
- 调用函数执行自身逻辑(如监听事件/等待条件)
- 当特定事件/条件被触发时,调用函数通过保存的指针调用对应的回调函数
- 回调函数执行具体响应逻辑,处理完成后流程继续
看简单的回调函数代码,加深理解:
c
#include <stdio.h>
// 定义函数指针类型
typedef void (*CallbackFunc)(int);
// 接收函数指针作为参数的函数
void event_handler(CallbackFunc callback) {
int event_id = 42;
callback(event_id); // 调用回调函数
}
// 自定义回调函数(需与 CallbackFunc 类型匹配)
void my_callback(int event_id) {
printf("Event %d triggered!\n", event_id);
}
int main() {
event_handler(my_callback); // 传递函数名(即函数地址)
return 0;
}
c
运行:
Event 42 triggered!
先理解主调函数、被调函数、回调函数这三个概念:
- 主调函数(调用者) :在调用
event_handler
时,main
是主调函数;在event_handler
调用my_callback
时,event_handler
成为主调函数。 - 被调函数 :
event_handler
被main
调用,因此是被调函数;而my_callback
被event_handler
调用,也是被调函数。 - 回调函数 :
my_callback
作为参数传递给event_handler
,并在其中被调用,因此是回调函数。
然后理解步骤:
- 1、
main
将my_callback
的函数地址作为参数传递给event_handler
- 2、
event_handler
生成事件ID(硬编码为42) - 3、通过函数指针调用
my_callback
,并传递事件ID - 4、回调函数执行具体的业务逻辑(打印事件信息)
2、改善代码
2.1 使用回调函数改造前
c
#define _CRT_SECURE_NO_WARNINGS 1
#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("****** 0.exit ******\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;
}
2.2 使用回调函数改造后
c
#define _CRT_SECURE_NO_WARNINGS 1
#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;
}
void calc(int (*pf)(int, int))
{
int x, y, t;
printf("输入操作数:");
scanf("%d %d", &x, &y);
t = pf(x, y);
printf("ret = %d\n", t);
}
int main()
{
int input = 1;
int ret = 0;
do {
printf("********************\n");
printf(" 1:add 2:sub\n");
printf(" 3:mul 4:div\n");
printf("****** 0.exit ******\n");
printf("请选择: ");
scanf("%d", &input);
switch (input)
{
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
3、qsort使用举例
3.1 使用qsort函数排序整型数据
c
#include <stdio.h> /* printf */
#include <stdlib.h> /* qsort */
// 回调函数
// const 保证数据的只读性
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;
// main作为主调函数,把回调函数的地址传参给调用qsort函数。
// qsort作为main的被调函数,去调用回调函数(int_cmp)
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;
}
c
运行:
0 1 2 3 4 5 6 7 8 9
3.2 使用qsort排序结构数据
c
#include <stdio.h> /* printf */
#include <stdlib.h> /* qsort */
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 test1()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi",30}, {"wangwu", 10} };
int sz = sizeof(s) / sizeof(s[0]);
printf("\n");
printf("年龄排序前:\n");
for (int i = 0; i < sz; i++)
{
printf("%d ", s[i].age);
}
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
printf("\n");
printf("年龄排序后:\n");
for (int i = 0; i < sz; i++)
{
printf("%d ", s[i].age);
}
}
// 按照名字来排序
void test2()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi",30}, {"wangwu", 10} };
int sz = sizeof(s) / sizeof(s[0]);
printf("\n");
printf("名字排序前:\n");
for (int i = 0; i < sz; i++)
{
printf("%s ", s[i].name);
}
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
printf("\n");
printf("名字排序后:\n");
for (int i = 0; i < sz; i++)
{
printf("%s ", s[i].name);
}
}
int main()
{
test1();
test2();
}
c
运行:
年龄排序前:
20 30 10
年龄排序后:
10 20 30
名字排序前:
zhangsan lisi wangwu
名字排序后:
lisi wangwu zhangsan
4、qsort函数的模拟实现
使用回调函数,模拟实现qsort(采用冒泡的方式)。
注意:在这里,第一次使用void*
的指针。
c
#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;
}
c
运行:
0 1 2 3 4 5 6 7 8 9
完。