1.回调函数是什么?
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
在上一节中我们所写的转移表中,我们虽然采用的是函数数组调用的形式,但是实际上是根据用户的输入来调取不同的函数,而我们今天学习了回调函数后,就可以把函数数组的部分优化掉,只采用一个函数作为接口,然后根据函数指针参数,调取不同的函数,更加的直观,简便。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void menu()
{
puts("*********************************");
puts("**********1.add 2.sub********");
puts("*********************************");
puts("**********3.mul 4.div********");
puts("*********************************");
puts("**************0.exit*************");
puts("*********************************");
}
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
//接口函数
void calc(int (*pf)(int, int))
{
printf("输入操作数:");
int x, y;
scanf("%d %d", &x, &y);
int ret = pf(x, y);
printf("%d\n", ret);
}
int main()
{
menu();
int x, y;
int input;
do
{
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);
case 0:
break;
default:
printf("选择错误\n");
break;
}
} while (input);
}
2.qsort()使用举例
qsort是c语言中stdlib.h库中的一个排序函数,可以根据我们所给的参数,排列大小,底层实现是快速排序,通过下面的参数,我们可以看到void* 说明qsort 函数是适用于所有任意类型的函数
base: 指向要排序的数组的第一个对象的指针
num: 数组中排序的元素数量
size: 数组中每个元素的大小(以字节为单位)
compar: 指向两个元素的函数指针
将两个指针作为参数(均转化为const void*)。函数通过返回定义元素的顺序:
2.1使用qsort函数排序整型数据
//这里需要注意参数,最好写成const void* 的形式 适配其他类型
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1) - (*(int*)p2);
}
int main()
{
int arr[] = { 1,3,5,4,8,9,12,2,3 };
int i = 0;
size_t sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
2.2使用qsort 排序结构数据
struct Stu
{
char name[20];
int age;
};
int cmp_stu_by_age(const void* p1, const void* p2)
{
return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age;
}
int cmp_stu_by_name(const void* p1, const void* p2)
{
return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
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_name);
for (int i = 0; i < sz; i++)
{
printf("%s\t", s[i].name);
printf("%d\n", s[i].age);
}
}
void test1()
{
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);
for (int i = 0; i < sz; i++)
{
printf("%s\t", s[i].name);
printf("%d\n", s[i].age);
}
}
int main()
{
test1(); //按照年龄排序
printf("\n");
test2(); //按照名字排序
return 0;
}
需要注意 strcmp 是string.h的库函数,专门用来比较两个字符串的大小的,且比较的是字典序,也就是依次比较ASCII码,返回值正好与我们构建的函数返回值规则一致。
3. qsort函数的模拟实现
使用回调函数,模拟实现qsort(采用冒泡的方式)
基本核心:
void qsort(
void* base,//base 指向了要排序的数组的第一个元素
size_t num, //base指向的数组中的元素个数(待排序的数组的元素的个数)
size_t size,//base指向的数组中元素的大小(单位是字节)
int (*compar)(const void* p1, const void* p2)//函数指针 - 指针指向的函数是用来比较数组中的2个元素的
)
{
if (compar(x, y) > 0)
{
//交换
}
}
实现代码:
注意其中的字节交换,以及排序时对于元素的变更,都是怎么具体实现的
void swap(void* p1, void* p2,size_t size)
{
//由于不知道类型,所以需要根据参数类型,一个一个字节交换字节里的值来达到元素交换的效果
for (int i = 0; i < size; i++)
{
char temp = *((char*)p1+i);
*((char*)p1+i) = *((char*)p2+i);
*((char*)p2 + i) = temp;
}
}
int int_cmp(const void * p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
//size_t 是字节类型 是无符号整型数据
void bubble(void* base, int count, size_t size, int (*cmp)(const void*, const void*))
{
int i, j;
i = j = 0;
for (i = 0; i < count-1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
//因为我们不知道传入的数组是什么类型的
// 所以就需要转成char* 然后移动 j*size来移动指针来达到选其他数组元素的目的
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0) // 大于0 说明 前者大于后者
{
swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
}
}
}
}
int main()
{
int arr[] = { 1,3,5,4,8,9,12,2,3 };
int i = 0;
int sz = sizeof arr / sizeof arr[0];
bubble(arr,sz , sizeof(int), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}