怎么制作网站论坛模板,陕西省高速集团建设网站,程显峰 wordpress,擦边球网站怎么做目录
思维导图
回调函数
案例1—计算器
案例2—qsort函数
关于qsort函数
演示qsort函数的使用
案例3—冒泡排序
整型数据冒泡排序
回调函数搞定各类型冒泡排序
cmp_int比较大小 cmp传参数
NO1.
NO2.
解决方案
交换swap
总代码 今天我们学习指针难点之回调函数…目录
思维导图
回调函数
案例1—计算器
案例2—qsort函数
关于qsort函数
演示qsort函数的使用
案例3—冒泡排序
整型数据冒泡排序
回调函数搞定各类型冒泡排序
cmp_int比较大小 cmp传参数
NO1.
NO2.
解决方案
交换swap
总代码 今天我们学习指针难点之回调函数。
首先我们用思维导图回顾一下前面的内容。
思维导图 回调函数 回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针地址作为参数传递给另一个函数当这个指针被用来调用其所指向的函数时我们就说这是回调函数。 回调函数不是由该函数的实现方直接调用而是在特定的事件或条件发生时由另外的一方调用的用于对该事件或条件进行响应。 案例1—计算器
就前文我们学习的计算器我们再用回调函数来解决一下
#define _CRT_SECURE_NO_WARNINGS 1
//计算器
#includestdio.h
void meau()
{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;
}
int main()
{int input 0;int x 0;int y 0;int ret 0;do{meau();printf(请选择);scanf(%d, input);switch (input){case 1:printf(请输入2个操作数);scanf(%d %d, x, y);ret Add(x, y);printf(ret%d\n, ret);break;case 2:printf(请输入2个操作数);scanf(%d %d, x, y);ret Sub(x, y);printf(ret%d\n, ret);break;case 3:printf(请输入2个操作数);scanf(%d %d, x, y);ret Mul(x, y);printf(ret%d\n, ret);break;case 4:printf(请输入2个操作数);scanf(%d %d, x, y);ret Div(x, y);printf(ret%d\n, ret);break;case 0:printf(退出游戏);break;default:printf(选择错误重新选择\n);break;}} while (input);return 0;
}
回调函数
#define _CRT_SECURE_NO_WARNINGS 1
//计算器
#includestdio.h
void meau()
{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(*p)(int, int))//函数指针传参
{int x 0;int y 0;printf(请输入两个操作数\n);scanf(%d %d, x, y);int ret p(x, y);//函数调用printf(ret%d\n, ret);
}
int main()
{int input 0;do{meau();printf(请选择\n);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(退出游戏);break;default:printf(选择错误重新选择\n);break;}} while (input);return 0;
} 解释如下 在main函数中没有直接去调用函数。而是把函数指针传参给另外的一个函数calc在calc内部使用函数指针调用通过函数指针就可以找到指针指向的函数此刻被指向的函数就是回调函数。 像上图所示通过calc函数调用Add函数指针,找到Add函数就把Add函数称为回调函数。 calc称为回调函数的机制 老板组长员工 案例2—qsort函数
那出了上面回调函数的案例还有一个经典回调函数的案例qsort qsort是一个库函数底层使用的是快速排序的方式对不同数据进行排序的。 这个函数可以直接使用。 这个函数可以用来排序任意类型的数据。 对数据进行排序方法很多 冒泡排序选择排序插入排序快速排序等等。 关于qsort函数
关于qsort函数的点---qsort - C Reference (cplusplus.com) 需要包含头文件#includestdlib.h 排序整型数组两个整型可以直接使用比较排序结构体数组两个结构体的数据可能不能直接使用比较 也就是不同类型的数据比较大小的方法是有差异的 最后一个参数排序不同数据的重要点需要封装不同的函数去比较不同的数据的大小 void qsort(void* base, //指向了待排序数组第一个元素的首地址size_t num, //待排序数组的元素个数size_t size,//每个待排序数组元素的大小int (*compar)(const void* e1, const void* e2));
//函数指针compar指向了一个函数这个函数是用来比较两个元素的大小
//e1和e2存放的是两个元素的地址
//在qsort内部调用这个函数指向这个函数这个函数就被称为回调函数
//
//qsort内部怎么排序我们不需要过多去探讨
//const也暂不做讲解//因为不知道要比较的元素类型所以我们使用void*指针的类型来统一存放各种类型的指针
那怎样通过元素地址去比较两个元素数据的大小呢 以int的数据为例将void*类型的数据强制转化成(int*),再作差。 当e1e2函数返回0的值 当e1e2函数返回0的值 当e1e2函数返回0 void compar_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
那有人询问为什么不直接对元素地址const void* e1和 const void* e2解引用 作为void*指针不能直接解引用。 void* 类型的指针—不能进行解引用操作符也不能进行-整数的操作 void* 类型的指针是用来存放任意类型数据的地址 void* 无具体类型的指针 void*和int*和char*一样都是指针类型 #includestdio.h
int main()
{char a x;char* pa a;int b 1;void* p b;//存放int*p a;//存放char*return 0;
}
演示qsort函数的使用
#includestdio.h
#includestdlib.h
void print(int arr[], int sz)
{int i 0;for (i 0; i 10; i){printf(%d , arr[i]);}
}void qsort(void* base, size_t num,size_t size,int (*compar)(const void*, const void*));void compar_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
void test1()
{int arr[] { 10,9,8,7,6,5,4,3,2,1 };int sz sizeof(arr) / sizeof(arr[0]);print(arr, sz);printf(\n);qsort(arr, sz, sizeof(arr[0]), compar_int);print(arr, sz);
}int main()
{test1();test2();return 0;
}以上我只是以整型为例结构体数据数组也是一样的逻辑大家可以自行分析。
下面结构体
#includestdio.h
#includestdlib.h
#includestring.h//void qsort(void* base,size_t num,size_t size,int (*compar)(const void*, const void*));struct Stu
{char name[20];int age;
};
//结构体数据怎么比较呢
//按照年龄比较
//按照名字比较//按照年龄
void compar_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)-age - ((struct Stu*)e2)-age;//return (*(struct Stu*)e1).age - (*(struct Stu*)e2).age;
}
void test2()
{struct Stu arr[] { {zhangsan,20},{lisi,30},{wangwu,12} };int sz sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), compar_stu_by_age);}
//按照名字
void compar_stu_by_name(const void* e1, const void* e2)
{return ((struct Stu*)e1)-name - ((struct Stu*)e2)-name;//return (*(struct Stu*)e1).name - (*(struct Stu*)e2).name;
}
void test2()
{struct Stu arr[] { {zhangsan,20},{lisi,30},{wangwu,12} };int sz sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), compar_stu_by_name);}int main()
{test2();return 0;
}只要qsort函数使用得当可以对任何数据进行排序
案例3—冒泡排序
使用回调函数模拟实现qsort(采用冒泡的方式
整型数据冒泡排序
(这种方式只能排列整数存在局限性
//冒泡排序
#includestdio.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 - 1 - i; j){if (arr[j] arr[j 1]){int tmp 0;tmp arr[j];arr[j] arr[j 1];arr[j 1] tmp;}}}
}
void print_arr(int arr[], int sz)
{int i 0;for (i 0; i sz; i){printf(%d , arr[i]);}
}
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);print_arr(arr, sz);
}
回调函数搞定各类型冒泡排序 经过分析冒泡排序我们得到 void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2)) cmp_int比较大小
以整型为例
int (*cmp)(const void* e1, const void* e2) e1是一个指针存放了一个要比较的元素的地址。 e2是一个指针存放了一个要比较的元素的地址。 e1指向的元素e2指向的元素返回0的数字。 e1指向的元素e2指向的元素返回0的数字。 e1指向的元素e2指向的元素返回0的数字。 cmp是函数指针指向一个我们程序想要待排序的数组。 将比较函数cmp_int的地址传给cmp即可。 //比较大小
void cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//这里就是将cmp_int的地址在调用函数bubble_sort时将其传过去即可。 cmp传参数
NO1.
有同学提出直接对待排序的数组首元素地址解引用找到e1的地址然后通过一个元素的大小或者1可以找到e2的地址可以吗当然不可以 作为void*指针不能直接解引用。 void* 类型的指针—不能进行解引用操作符也不能进行-整数的操作 void* 类型的指针是用来存放任意类型数据的地址 void* 无具体类型的指针 void*和int*和char*一样都是指针类型
NO2.
有同学又提出那将void*的指针强制转换成我们想要的int*或double*等再1可以吗 不可以理由就是强制转换存在在于我们公共的bubble_sort排序函数中时不能随着待排序的数组数据类型不同而改变我们只能改变不同数据类型的不同比较方法。 解决方案 //if(arr[j]arr[j1])if (cmp( (char*)basej*size,(char*)base(j1)*size )0){int tmp 0;tmp arr[j];arr[j] arr[j 1];arr[j 1] tmp;}
交换swap
当我们只知道元素的起始地址并不知道元素的类型所以我们并没有合适的中间值类型tmp创建。所以我们换一种方法。 我们已知元素e1和e2的起始地址和每个元素的大小。 那我们可以用一个一个char类型的数据交换用for循环 直到每个元素的大小size结束也就是元素交换完成。 //交换数据
void change(char* buf1, char* buf2,size_t size)
{char i 0;for (i 0; i size; i){char tmp 0;tmp *buf1;*buf1*buf2;*buf2 tmp;buf1;//*buf1buf2;//*buf2}
}
总代码
//冒泡排序
#includestdio.h
void print_arr(int arr[], int sz)
{int i 0;for (i 0; i sz; i){printf(%d , arr[i]);}
}void bubble_sort(void* base, size_t num, size_t size,int (*cmp)(const void* e1, const void* e2))
{int i 0;for (i 0; i num - 1; i){int j 0;for (j 0; j num - 1 - i; j){//if(arr[j]arr[j1])if (cmp( (char*)basej*size,(char*)base(j1)*size )0){change((char*)base j * size, (char*)base (j 1) * size, size);}}}
}
//交换数据
void change(char* buf1, char* buf2,size_t size)
{char i 0;for (i 0; i size; i){char tmp 0;tmp *buf1;*buf1*buf2;*buf2 tmp;buf1;//*e1buf2;//*e2}
}
//比较大小
void cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;//0
}void test1()
{int arr[10] { 9,8,7,6,5,4,3,2,1,0 };int sz sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);printf(\n);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main()
{test1();
}
当然我们也可以用结构体类型去测试一下 试试
✔✔✔✔✔最后感谢大家的阅读若有错误和不足欢迎指正旗鼓相当
代码------→【gitee:唐棣棣 (TSQXG) - Gitee.com】
联系------→【邮箱2784139418qq.com】