零基础自学C语言|深入理解指针 ④
📌回调函数
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
在上一节我们写的计算机的实现的代码中,有的代码是重复出现的,其中虽然执行计算的逻辑是区别的,但是输入输出操作是冗余的,有没有办法,简化一些呢?
因为红色框中的代码,只有调用函数的逻辑是有差异的,我们可以把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这里其实使用的就是回调函数的功能。
修改前:
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 menu()
{
printf("*****************************\n");
printf("*****************************\n");
printf("**** 1.加法 2.减法 ****\n");
printf("**** 3.乘法 4.除法 ****\n");
printf("**** 0.退出 **************\n");
printf("*****************************\n");
}
int main()
{
int a = 0;
int x = 0, y = 0;
do
{
menu();
printf("请输入数字进行计算->\n");
scanf("%d", &a);
switch (a)
{
case 1:
printf("输入操作数\n");
scanf("%d%d", &x, &y);
printf("结果为:%d\n", Add(x, y));
break;
case 2:
printf("输入操作数\n");
scanf("%d%d", &x, &y);
printf("结果为:%d\n", Sub(x, y));
break;
case 3:
printf("输入操作数\n");
scanf("%d%d", &x, &y);
printf("结果为:%d\n", Mul(x, y));
break;
case 4:
printf("输入操作数\n");
scanf("%d%d", &x, &y);
printf("结果为:%d\n", Div(x, y));
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (a);
return 0;
}
修改后:
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 menu()
{
printf("*****************************\n");
printf("*****************************\n");
printf("**** 1.加法 2.减法 ****\n");
printf("**** 3.乘法 4.除法 ****\n");
printf("**** 0.退出 **************\n");
printf("*****************************\n");
}
void Cal(int(*pf)(int, int))
{
int x = 0, y = 0;
printf("输入操作数\n");
scanf("%d%d", &x, &y);
printf("结果为:%d\n", pf(x, y));
}
int main()
{
int a = 0;
do
{
menu();
printf("请输入数字进行计算->\n");
scanf("%d", &a);
switch (a)
{
case 1:
Cal(Add);
break;
case 2:
Cal(Sub);
break;
case 3:
Cal(Mul);
break;
case 4:
Cal(Div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (a);
return 0;
}
📌qsort的使用
qsort是一种用来排序的库函数,头文件是<stdlib.h>。相比冒泡排序,它的好处是可以排序任意数据类型,而冒泡排序只能排序整型。它的底层代码使用的是快速排序。
qsort函数一共有四个参数
- 指针参数,代表首元素的地址
- 整型参数,代表要排序的元素的个数
- 整型参数,代表要排序的元素的大小
- 函数指针参数,提供排序规则,当传入两个元素时,保证:(1)两个元素相等,返回0。(2)两个元素顺序反了,返回负值。(3)两个元素顺序正确,返回正值。
光说无用,我们举一个简单的例子。
📍qsort排序整形:
int int_cmp(const void* p1, const void* p2)//排序规则
{
return(*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = { 5,9,1,3,0,7,6,4,2,8 };
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), int_cmp);//qsort函数
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
📍qsort排序结构体数据
了解了qsort的基本用法后,我们就可以做一些冒泡排序做不到的事:
struct stu//学生
{
char name[20];//名字
int age;//年龄
};
struct stu s[] = { {"zhangsan",38},{"lisi",40},{"wangwu",18} };
int cmp_stu_by_name(const void* p1, const void* p2)//名字排序的方法
{
return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
int cmp_stu_by_age(const void* p1, const void* p2)//年龄排序的方法
{
return ((struct stu*)p1)->age - ((struct stu*)p2)->age;
}
void test1()//年龄排序
{
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
void test2()//名字排序
{
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
test1();
test2();
return 0;
}
值得注意的是,名字排序的方法中,用到了一个新的函数:strcmp
这个函数是用来排序字符串的
这个函数需要传入两个字符串参数
它根据两个字符串第一个不同的相同位的ASCII码值来排序,例如“zhangsan”和“lisi”的第一位分别是“z”和“l”而这两个字符不相同,则根据他们的ASCII码值来排序,如果第一位相同,就找第二位,以此类推。
年龄排序:
名字排序:
📌qsort函数的模拟实现
学习了qsort函数的基本逻辑以后,我们是否能利用冒泡排序写出一个自己的qsort函数呢?
答案是可以的。
下面是参考代码,并附上int类型排序的测试:
int int_cmp(const void* p1, const void* p2)//int类型排序规则
{
return *(int*)p1 - *(int*)p2;
}
void _cmp(char* px, char* py, int size)//交换函数
{
for (int i = 0; i < size; i++)
{
char cmp = 0;
cmp = *px;
*px = *py;
*py = cmp;
px++;
py++;
}
}
void my_bubble(void* base, int count, int size, int (*cmp)(const void* p1, const void* p2))//排序函数
{
for (int i = 0; i < count - 1; i++)
{
for (int j = 0; j < count - i - 1; j++)
{
//利用char类型的指针只有一个字节,交换每个字节上的数字,实现将任意大小的数值交换
if (cmp((char*)base+j*size, (char*)base+(j+1)*size) > 0)
{
_cmp((char*)base + j * size, (char*)base + (j + 1) * size, size);//将交换的函数封装
}
}
}
}
int main()//函数测试
{
int arr[] = { 3,6,7,9,1,2,0,4,5,8 };
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
my_bubble(arr, sz, sizeof(arr[0]), int_cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
排序测试结果: