回调函数
什么是回调函数?
回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
例子
使用回调函数前的代码:
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:add2:sub \n");
printf(" 3:mul4: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;
}
使用回调函数后的代码:
#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 ret = 0;
int x, y;
printf(“输⼊操作数:”);
scanf(“%d %d”, &x, &y);
ret = pf(x, y);
printf(“ret = %d\n”, ret);
}
int main()
{
int input = 1;
do
{ printf("*************************\n");
printf(" 1:add2:sub \n");
printf(" 3:mul4:div \n");
printf("*************************\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;
}
解读:这里我们运用了回调函数,使得重复的部分得到了优化,更简洁了一些
我们这里调⽤的函数的地址以参数的形式传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能。
qsort排序
形式
使用qsort函数排序整形数组
注意这里在调用qsort函数的时候里面的参数要一一对应,前三个参数都没什么问题,但是第四个参数-- 比较大小的函数是需要我们自己来提供的,正如上面的代码,我们为了比较整形数组中相邻元素的大小,从而自己写了一个函数cmp_int
来进行整形数组元素大小的比较
cmp_int
: 在写这个函数时注意 这里传输的指针是void*
类型的,因为你是不知道传输来的到底是什么类型的,所以这里就干脆写成了void*
,正是因为这里是void*
,所以不能直接进行解引用操作,所以在这里我们对其进行了强制类型转换,把它转换成了int*
类型,这里是根据升序来排列的,如果想排成降序就将比较函数中的逻辑调换就可以了
使用qsort函数排序结构体
结构体指针变量
因为下面我们要用到结构体指针变量,所以我们先来认识一下结构体变量:
按名字排序结构体:
解读: 在按照名字对结构体进行比较的时候和整型有所不同,因为这里的名字是属于字符串,所以这里在比较字符串的时候我们就用到了strcmp这个函数,注意这里可以用.
来访问结构体中的元素,但是这里需要解引用,也可以用->
来访问结构体中的元素,这个时候就相当于已经解引用了,这两种写法都可以。
strcmp函数形式: strcmp(字符串1,字符串2);
注意要包含头文件<string.h>
按年龄排序结构体
这里在比较年龄时就是整形的比较,和整形排序的道理是一样的。
冒泡排序改造模拟qsort函数
前面我们学习了冒泡排序,但是冒泡排序只是只是针对整形数组,要想用冒泡排序来实现其他类型数组的排序就需要对冒泡排序进行改造
主体框架
这里我们仿照qsort函数,也是对冒泡函数传了四个参数
冒泡函数
这里的前半部分和冒泡排序没什么大的区别,只是这里要比较两个元素大小的时候,我们通过字符指针变量来找到数组元素的地址,这里我们把类型强转为char*
类型,因为一个整形数组中的元素是占四个字节,并且字符指针变量每加上一就是跳过一个字节,这里的width代表的是一个整形数组元素的长度,也就是四个字节,所以这里的 (char*)base + j * width
和(char*)base + (j + 1) * width
就分别代表了第j个和第j+1个元素的地址
通过这种思路我们就可以找到不同类型元素的地址,从而来对不同的类型的数组进行大小比较
如果cmp函数返回值大于0的话就说明arr[j] > arr[j+1]
,这个时候就对这两个元素进行交换,这时候就来到了交换函数:
这里的n其实就是指的width
下面我们结合下面的草图对这个函数进行理解:
因为我们知道一个整形是占四个字节的,并且我们上面对整形元素进行了统一,强转成了char*类型
,这个时候只要将两个元素的四个字节内的内容一一交换即可实现效果
打印数组元素
实现效果:
注意这里是用到了回调函数