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("*************************\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:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \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;
}
可以看到,回调函数的版本加了以下的函数:
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);
}
这个函数的参数部分是函数指针,所以我们就可以利用这个函数,给这个函数传参,从而实现其它函数的调用。
在使用回调函数前的代码中,这个部分是显得比较多余的
在我们使用一个函数将输入输出等操作装在一个函数,并且通过给这个函数传参来进行相应的函数的调用后,就会看起来整洁了许多。
我们知道函数名代表函数地址,所以传参部分就是相应要调用的函数名字
上面的add、sub等函数就是通过函数指针去调用的函数,就是回调函数
2. qsort使用
qsort是一个库函数
头文件为:
#include<stdlib.h>
参数分别为:
数组名
数组元素个数
数组类型的大小(如整形数组的数组类型的大小为 siefof(int))
比较函数
注意:这个比较函数是自定义的,即要根据比较的元素的类型,升序或者降序来写。
如整形的比较函数:
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
这里的void* 表示p1和p2的类型是没有定义的,就是可以接收各种类型的指针,如int*、char* 。
该代码为升序,如果想变成降序,只需要将p1和p2调换即可。
对于不同类型的数组排序,qsort函数的格式都是相同的,唯一不同在于比较函数中的返回值。
2.1 使用qsort函数排序整型数据
#include <stdio.h>
#include<stdlib.h>
//qsort函数的使⽤者得实现⼀个比较函数
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 1, 5, 7, 6, 2, 3, 0, 9, 8, 4 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(int), int_cmp);
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
运行结果:
2.2 使用qsort排序结构数据
#include<stdio.h>
#include<stdlib.h>
struct Stu //学⽣
{
char name[10];//名字
int age;//年龄
};
//按照年龄来比较
int cmp_stu_by_age(const void* s1, const void* s2)
{
return ((struct Stu*)s1)->age - ((struct Stu*)s2)->age;
}
//strcmp - 专门⽤来比较两个字符串⼤小的库函数
//按照名字来比较
int cmp_stu_by_name(const void* s1, const void* s2)
{
return strcmp(((struct Stu*)s1)->name, ((struct Stu*)s2)->name);
}
//按照年龄来排序
void test2()
{
struct Stu s[] = { {"zhangsan", 17}, {"lisi", 19}, {"wangwu", 16} };
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 ", s[i].name);
}
printf("\n");
}
//按照名字来排序
void test3()
{
struct Stu s[] = { {"zhangsan", 17}, {"lisi", 19}, {"wangwu", 16} };
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 ", s[i].name);
}
printf("\n");
}
int main()
{
test2();
test3();
return 0;
}
运行结果:
3. qsort函数的模拟实现
这里要使用到回调函数,并且用冒泡排序的方法排序
#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类型 一个字节一个字节的交换 所以能适应各种类型的交换 控制个数即可
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)
{
//比较的结果大于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;
}
这里的冒泡排序中的参数就是函数指针,在冒泡排序中对函数指针指向的指针进行了调用。