#C语言——学习攻略:深挖指针路线(五)--回调函数,qsort函数,qsort函数的模拟实现
🌟菜鸟主页:@晨非辰的主页
👀学习专栏:《C语言学习》
💪学习阶段:C语言方向初学者
⏳名言欣赏:"暴力解法是上帝给的,优化解法是魔鬼教的。"
目录
1. 回调函数
1.1 什么是回调函数
1.2 回调函数试验:改造一般方式实现计算器功能
2. qsort函数
2.1 介绍qsort函数
2.2 qsort函数排序整型数据
2.3 qsort函数排序结构体数据
3. qsort函数模拟实现
1. 回调函数
1.1 什么是回调函数
--简单说,回调函数就是由一个通过函数指针调用的函数。
--如果把函数的指针(地址)作为参数传给另一个函数,当函数指针被用来调用所指向的函数时,这个被调用的函数就是回调函数。回调函数不由该函数的实现方式直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
--回调函数的一大特点:回调函数简化代码避免重复代码——>如果多个函数需要类似的逻辑,回调可以提取公共部分,减少重复。
--就比如上一篇博客分享的:一般方式实现计算器功能的代码,就可以使用回调函数,来看一下吧:
int Add(int x, int y)//这里的Add就是回调函数
{return x + y;
}void test(int(*pf)(int, int))//参数时函数指针,因为要接收函数地址
{int r = pf(10, 20);printf("%d\n", r);
}int main()
{test(Add);//将Add函数的地址传给test函数return 0;
}
1.2 回调函数试验:改造一般方式实现计算器功能
--在前面的博客有分享怎么一般方式来实现计算器的基本功能,但是部分代码重复较高,今天就用回调函数来优化:
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(*pa)(int, int))
{int ret = 0;int x = 0;int y = 0;printf("请输入操作数:");scanf("%d %d", &x, &y);ret = pa(x, y);printf("%d\n", ret);
}void menu()
{printf("****************************\n");printf("******1.Add 2.Sub******\n");printf("******3.Mul 4.Div******\n");printf("********* 0.exit *********\n");printf("****************************\n");
}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;
}
--看完这串代码,可以知道:里面的像add、sub、mul、div这四个函数都是回调函数
--从图中可以清楚的看到,add等函数将地址传给calc函数。calc函数的参数是函数指针用来接收地址,在内部调用函数。
--这样就看到了回调函数到底用来做什么:把调用的函数的地址以参数形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数。
2. qsort函数
2.1 介绍qsort函数
-- qsort 是C标准库(<stdli.h>)提供的快速排序(Quick Sort)函数,用于对任意类型的数组进行排序。
--优点:
1. 通用性强(支持任意数据类型): 2. 灵活性高(自定义排序规则):
基本类型(
int
,float
,char
) 升序 / 降序指针类型(
char*
,void*
) 按字符串长度排序结构体(
struct
) 多关键字排序(如先按年龄,再按姓名)动态分配的数组
--qsort函数返回类型、参数:
void qsort(void *base, ——>指向要排序的数组首元素的指针。size_t nmemb, ——>数组元素个数size_t size, ——>每个元素的字节大小int (*compar)(const void *, const void *) ——>比较函数指针,用于比较数组中的两个元素
);
--对于函数参数为两个void,可以接收任意类型的指针。
返回值:
<0
:a
应排在b
前面(升序)。
=0
:a
和b
相等。
>0
:a
应排在b
后面(降序)。
2.2 qsort函数排序整型数据
//升序,cmp_int1用来比较两个整形数据
int cmp_int1(const void* p1, const void* p2)
{return (*(int*)p1) - (*(int*)p2);//void*指针不能直接进行解引用,强转为整型指针类型//默认升序是因为 a - b 的写法符合直观的“从小到大”逻辑
}
//降序
int cmp_int2(const void* p1, const void* p2)
{return (*(int*)p2) - (*(int*)p1);//反转逻辑
}
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", arr[i]);}printf("\n");
}
int main()
{int arr[] = { 3,1,5,8,7,9,2,4,6,0 };int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);//使用qsort函数的,需要自己写一个比较函数qsort(arr, sz, sizeof(arr[0]), cmp_int1);printf("升序:");print_arr(arr, sz);qsort(arr, sz, sizeof(arr[0]), cmp_int2);printf("降序:");print_arr(arr, sz);
}
2.3 qsort函数排序结构体数据
#include<string.h>
#include<stdlib.h>
struct Stu
{char name[30];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* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);//strcmp是专门用来比较两个字符串的大小的
}void print_stu(struct Stu arr[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%s:%d\n", arr[i].name, arr[i].age);}printf("\n");
}//按照年龄来排序
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);print_stu(arr, sz);
}//按照名字来排序
void test3()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);print_stu(arr, sz);
}
int main()
{test2();test3();return 0;
}
3. qsort函数模拟实现
--使用回调函数,模拟实现qsort(采用冒泡的方式)。(首次使用void*类型)
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 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){_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;
}
往期复习:
1. #C语言——学习攻略:深挖指针路线(一)--指针变量、地址、意义与指针运算
2. #C语言——学习攻略:深挖指针路线(二)--const修饰、野指针分析、断言和指针的作用
3. #C语言——学习攻略:深挖指针路线(三)--数组与指针的结合、冒泡排序
4.#C语言——学习攻略:深挖指针路线(四)--字符指针变量,数组指针变量,二维数组传参的本质,函数指针变量,函数指针数组
结语:本篇内容就到这里了,主要分享了指针变量类型的一些内容,后续仍会分享指针的相关知识;指针的内容需要反复研读 ,如果这篇文章对你的学习有帮助的话,欢迎一起讨论学习,你这么帅、这么美给个三连吧~~~