目录
一.回调函数:
1.回调函数的定义:
2.回调函数的使用:
3.qsort函数的使用:
4.利用回调函数模拟实现qsort函数:
二.总结:
博客主页:张栩睿的博客主页
欢迎关注:点赞+收藏+留言
系列专栏:c语言学习
家人们写博客真的很花时间的,你们的点赞和关注对我真的很重要,希望各位路过的朋友们能多多点赞并关注我,我会随时互关的,欢迎你们的私信提问,也期待你们的转发!
希望大家关注我,你们将会看到更多精彩的内容!!!
一.回调函数:
1.回调函数的定义:
我们都知道,函数的调用除了我们最基础的调用方式之外,通过函数指针我们也可以实现对函数的调用。函数指针调用函数的函数,就称为回调函数。
如果你把函数的地址作为参数(即使用指针)传递给另一个函数 ,且当这个指针调用了它指向的函数时,我们就把这样的使用方式称作回调函数。
2.回调函数的使用:
实例1:
-
//函数1:
-
//函数1使用了这样的调用方式,就被称为回调函数
-
void
test(
)
-
{
-
printf(
"test\n");
-
}
-
-
//函数2:
-
//使用函数指针接收,并对函数test进行调用:
-
void
TEST(
void(*p)())
-
{
-
//使用函数指指针调用test函数:
-
p();
-
-
printf(
"TEST\n");
-
}
-
-
int
main(
)
-
{
-
//将函数地址传递给另一个函数:
-
TEST(test);
-
-
return
0;
-
}
在这个过程中,函数 test 就被作为函数参数传递了出去,并且在函数 TEST 中被函数指针接收,且该指针调用了它指向的 test 函数,于是我们就可以说 test 函数是回调函数。
实例2:
我们之前通过转移表简化了计算器代码的书写计算器的书写,我们是否可以用回调函数简化呢?
-
void menu()
-
{
-
printf(
"*******************************\n");
-
printf(
"****** 1. add 2. sub *****\n");
-
printf(
"****** 3. mul 4. div *****\n");
-
printf(
"****** 0. exit *****\n");
-
printf(
"*******************************\n");
-
}
-
-
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))
-
{
-
int x =
0;
-
int y =
0;
-
int ret =
0;
-
printf(
"请输入两个操作数:>");
-
scanf(
"%d %d", &x, &y);
-
ret =
pf(x, y);
-
printf(
"%d\n", ret);
-
}
-
-
int main()
-
{
-
int input =
0;
-
-
do
-
{
-
menu();
-
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;
-
}
3.qsort函数的使用:
我们之前学过使用冒泡排序,用冒泡排序写一个函数来对整形数组进行升序降序,但是局限就是只能对整形,对其他类型无法排序。而qsort函数对于任意类型都可以排序,整形,字符型,甚至结构体型都可以,那么我们该如何·使用·qsotr函数呢?
库函数 qsort 是基于快速排序法实现的一个排序函数。
我们在cplusplus上看看用法:
函数括号里到底传入了什么参数呢?
这里的compare函数如何书写呢?
不难发现,在升序排序中,compare是将两个要排序的元素的地址作为参数,将他们比较大小的差作为返回值。
如果结果>0,返回正数
如果结果=0.返回0
如果结果<0,返回负数
那么就会有同学问了,那么降序怎么办?
如果降序的话就让两个值交换位置相减即可。
那么就会有同学问了,那个void*是什么类型,不同类型如何转化?
下面我们就来看看结构体的排序吧!
-
struct Stu
-
{
-
char name[
20];
-
int age;
-
};
-
-
//按照学生的年龄来排序
-
int cmp_stu_by_age(const void* e1, const void* e2)
-
{
-
return ((
struct Stu*)e1)->age - ((
struct Stu*)e2)->age;
-
}
-
-
//按学生的姓名排序
-
int cmp_stu_by_name(const void* e1, const void* e2)
-
{
-
return strcmp(((
struct Stu*)e1)->name, ((
struct Stu*)e2)->name);
-
}
-
-
void test2()
-
{
-
struct Stu s[
3] = { {
"zhangsan",
20}, {
"lisi",
50}, {
"wangwu",
33} };
-
int sz =
sizeof(s) /
sizeof(s[
0]);
-
//qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
-
qsort(s, sz,
sizeof(s[
0]), cmp_stu_by_name);
-
}
这里需要注意的是strcmp的使用,他对于两个字符串的每个字符一一对比,如果大于返回正数,等于就往后继续比,小于就返回负数。这和我们要返回的数值正好对应。
下面,我们就通过冒泡排序来实现qsort函数吧!
4.利用回调函数模拟实现qsort函数:
首先我们回忆一下冒泡排序:
-
#include <stdio.h>
-
-
void bubble_sort(int arr[],int sz)
-
{
-
int i =
0;
-
for (i =
0; i < sz -
1; i++)
-
{
-
int j =
0;
-
for (j =
0; j < sz - i -
1; j++)
-
{
-
if (arr[j] > arr[j +
1])
-
{
-
int tmp = arr[j];
-
arr[j] = arr[j +
1];
-
arr[j +
1] = tmp;
-
}
-
}
-
}
-
}
-
-
int main()
-
{
-
int arr[] = {
1,
2,
3,
4,
0,
8,
9,
7,
6,
5 };
-
int sz =
sizeof(arr) /
sizeof(arr[
0]);
-
bubble_sort(arr, sz);
-
int i =
0;
-
for (i =
0; i < sz; i++)
-
{
-
printf(
"%d ", arr[i]);
-
}
-
return
0;
-
}
这样的冒泡排序已经写死了整形,不能比较其他类型。
我们设计一个兼容可以排序整型,又可以排字符型和结构体的呢
模拟qsort的思想,利用冒泡的形式
-
void swap(char* buf1, char* buf2, int width)
-
{
-
int i =
0;
-
for (i =
0; i < width; i++)
-
{
-
char tmp = *buf1;
-
*buf1 = *buf2;
-
*buf2 = tmp;
-
buf1++;
-
buf2++;
-
}
-
}
-
int cmp_int(void* e1, void* e2)
-
{
-
return *(
int*)e1 - *(
int*)e2;
-
}
-
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* e1, const void* e2))
-
{
-
int i =
0, j =
0;
-
for (i =
0; i < sz -
1; i++)
-
{
-
for (j =
0; j < sz -
1 - i; j++)
-
{
-
if (cmp((
char*)
base + j * width, (
char*)
base + (j +
1) * width) >
0)
-
{
-
swap((
char*)
base + j * width, (
char*)
base + (j +
1) * width,width);
-
}
-
}
-
}
-
}
-
int main()
-
{
-
//整数:
-
int arr[
10] = {
9,
8,
7,
6,
5,
4,
3,
2,
1,
0 };
-
int sz =
sizeof(arr) /
sizeof(arr[
0]);
-
bubble_sort(arr, sz,
sizeof(arr[
0]), cmp_int);
-
for (
int i =
0; i < sz; i++)
-
printf(
"%d ",arr[i]);
-
}
这里有两处非常重要的点:
首先是这个冒泡排序:
for (i = 0; i < sz - 1; i++) { for (j = 0; j < sz - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { swap((char*)base + j * width, (char*)base + (j + 1) * width, width); } } }
因为我们不知道我们会对什么类型的变量排序,所以我们用char*强转指针,使这个指针的步长为一,后面就可以显现出width(类型大小)的作用了:
因为我们不知道类型,不能用[]下标访问传过来的数组,就只能用指针的形式,通过指针+-整数来访问这个数组的每个元素。我们根据+width的倍数,正好就是这个类型的步长。
其次是这个swap交换函数:
void swap(char* buf1, char* buf2, int width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } }由于我们不知道要交换元素的类型,所以我们通过变量中拥有最小单位:一个字节的char类型来依次交换每个字节来实现元素的交换,这样是不是很简便呢?
我们在来看一看结构体类型的交换:
-
void swap(char* buf1, char* buf2, int width)
-
{
-
for (
int i =
0; i < width; i++)
-
{
-
char tmp = *buf1;
-
*buf1 = *buf2;
-
*buf2 = tmp;
-
buf1++;
-
buf2++;
-
}
-
}
-
struct stu
-
{
-
char name[
20];
-
int age;
-
};
-
int cmp_stu_by_name(void* e1, void* e2)
-
{
-
return (strcmp(((
struct stu*)e1)->name, ((
struct stu*)e2)->name));
-
}
-
void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(void* e1, void* e2))//这里等下看一下
-
{
-
int i, j;
-
for (i =
0; i < sz; i++)
-
{
-
for (j =
0; j < sz -
1 - i; j++)
-
{
-
if (cmp((
char*)
base + j * width, (
char*)
base + (j +
1) * width) >
0)
-
{
-
swap((
char*)
base + j * width, (
char*)
base + (j +
1) * width,width);
-
}
-
}
-
}
-
}
-
int main()
-
{
-
//结构体排序
-
struct stu s[
3] = { {
"zhangsan",
20}, {
"lisi",
50}, {
"wangwu",
33} };
-
int sz =
sizeof(s) /
sizeof(s[
0]);
-
//bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
-
bubble_sort(s, sz,
sizeof(s[
0]), cmp_stu_by_name);
-
}
结果:
不难发现,bubble_sort函数,swap函数都一模一样,都是写好了的,所以我们只要自己写出那个回调函数cmp函数(比较大小) 即可,这就是qsort的方便之处。
二.总结:
通过qsort函数的学习,我们对回调函数有了更深的理解。这里我们在复习一下qsort里的参数。
今天我们算是给我们的指针升级之路画上了一个完美的句号,至此我们关于指针的进阶的学习就告一段落了。 辛苦各位小伙伴们动动小手,三连走一波 最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!
转载:https://blog.csdn.net/qq_74310471/article/details/128597957