1 回调函数的定义
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
概念有些抽象,接下来通过学习qsort函数以及其模拟实现来促进理解。
2 qsort函数
qsort函数是用快速排序方法能对数组、结构体等按提供的规则进行排序的一个库函数。
c
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
该函数包含四个参数:
base是指向待排序内容的首元素地址;num是待排序内容的元素个数;size是待排序内容的一个元素大小,单位字节;- 函数指针
compar是指向比较函数,需要自行编写,实现对待排序内容中两个元素的比较。
因为qsort函数并不知道你会传入一个什么样的数据类型,所以就需要你自己先写好一个比较函数,再传给qsort使用。比较函数的类型、参数与返回值都有规定,见参考链接。
qsort函数更详细说明的参考链接:legacy.cplusplus.com/reference/c...
下面是一个使用qsort函数排序整型数组的示例:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
//const在*左侧,确保指针指向的内容不会被修改
{
return *((int*)e1) - *((int*)e2);
//要排序的是整形数组,元素是整形,这里先将void*类型强转成int*类型
//强转成与元素对应的指针类型,解引用才能得到正确的结果
//这里return返回相减的差,正好符合比较函数的返回值要求
}
int main()
{
int arr[] = { 5,6,4,1,8,9,2,3,7 };
int num = sizeof(arr) / sizeof(arr[0]);
qsort(arr, num, sizeof(arr[0]), cmp_int);
}
下面是一个使用qsort函数排序结构体数组的示例:
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
struct Stu
{
char name[20];
int age;
};
void test3()//这里展示了两种访问结构体指针指向内容的方法
{
struct Stu s = { "cuihua", 18 };
struct Stu* ps = &s;//结构体指针变量
printf("%s\n", (*ps).name);
printf("%s\n", ps->name);
//结构体变量.成员名
//结构体指针变量->成员名
}
//按照名字比较大小
//e1是指向一个结构体数据的,e1是指向另外一个结构体数据的
//名字是字符串,字符串的比较使用strcmp
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp((*(struct Stu*)e1).name, (*(struct Stu*)e2).name);
}
int cmp_stu_by_name1(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来比较
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//测试qsort函数排序结构体数据 - 按名字比较
void test2()
{
struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
//{"lisi", 35}, {"wangwu", 12},{"zhangsan", 18}
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
//测试qsort函数排序结构体数据 - 按年龄比较
void test4()
{
struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
//{{"wangwu", 12}, "zhangsan", 18},{"lisi", 35}
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{
test2();
test4();
return 0;
}
3 模拟实现
第2节解释了qsort函数的参数及运行方式,接下来尝试模拟实现。这里排序方法采用更熟悉的冒泡排序,而非qsort的快速排序。
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
void Swap(char* buf1, char* buf2, size_t sz)
{
for (int i = 0; i < sz; i++)
{
int tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, size_t n, size_t sz, int (*cmp)(const void*, const void*))
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (cmp((char*)base + sz * j, (char*)base + sz * (j + 1)) > 0)
{
Swap((char*)base + sz * j, (char*)base + sz * (j + 1), sz);
}
}
}
}
int cmp_int(const void* e1, const void* e2)
{
return *((int*)e1) - *((int*)e2);
}
int main()
{
int arr[] = { 5,6,4,1,8,9,2,3,7 };
int num = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, num, sizeof(arr[0]), cmp_int);
}
观察上述的示例,并回忆回调函数的定义。
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。
比较函数 cmp_int通过函数指针的形式传入bubble_sort函数并在其内部被调用,比较函数cmp_int就是一个回调函数。其实第2节示例中的比较函数们也都是回调函数。
可见,回调函数在实现一些复杂功能时具有独到的优势。想想如果不用回调函数应该是不太好实现相同的功能的。