指针4
1.回调函数是什么2.qsort使用举例3.qsort函数的模拟实现
1.回调函数是什么
回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。
回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
上一篇中我们写的计算机的实现的代码中,代码是重复出现的,其中虽然执⾏计算的逻辑是区别的,但是输⼊输出操作是冗余的,有没有办法,简化⼀些呢?
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("*********************\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;}
我们发现调用的函数都是int (int,int)类型的,我们可以把调⽤的函数的地址以参数的形式传递过去,使⽤这样类型的函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能。
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 x, y; int input = 1; int ret = 0; do { printf("*********************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4: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;}
因为上方代码,只有调⽤函数的逻辑是有差异的,我们可以把调⽤的函数的地址以参数的形式
传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函
数的功能
注意区分这和我们在【C语言篇】深入理解指针3(附转移表源码)中实现的转移表,这里使用的是回调函数,但在转移表中我们使用的是函数指针数组
2.qsort使用举例
默认升序
quick sort快速排序的思想,冒牌排序是拍整形数据,这个是对任意类型的排序,
1.指向待排序数组第一个元素的指针
2.数组元素个数
3.数组中一个元素的大小,单位是字节
4.函数指针 指向函数的地址,指向比较2个元素的函数的指针,它指向的是一个需要两个无类型指针作为参数的函数
qsort函数排序整型数据
#include <stdio.h>#include <stdlib.h> //qosrt函数的使⽤者得实现⼀个⽐较函数 int int_cmp(const void * p1, const void * p2){ return (*( int *)p1 - *(int *) p2);//p1 p2是名字}int main(){ int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 }; int i = 0; qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp); //sizeof(int)=sizeof(arr[0]) for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++) { printf( "%d ", arr[i]); } printf("\n"); return 0;}
使用qsort排序结构数据
struct Stu //学⽣{ char name[20];//名字 int age;//年龄};//假设按照年龄来⽐较 e1e2一交换就是降序int cmp_stu_by_age(const void* e1, const void* e2){//强转为结构体指针,找成员,整数比较做差 return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;}//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的,比较对应位置上字符的ASCII的大小//假设按照名字来⽐较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[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);}//按照名字来排序void test3(){ struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);}int main(){ test2(); test3(); return 0;}
3.qsort函数的模拟实现
qsost底层采用的是快速排序的方法,在这里我们使用更简单的冒泡排序的排序算法来模拟实现qsort函数,这里使用void*的指针,以实现泛型编程,换句话说,使⽤回调函数,模拟实现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 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++)//趟数内部比较{ //都是base,所以强转,size是宽度单位字节,arr首元素的地址传给base //if后面的(是到最后size的 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;}
qsort的实现思路推荐博客