1、回调函数 1.1、回调函数的概念
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
1.2、回调函数的使用 1.2.1、 案例一:理解:如果拿到一个a函数的地址,通过a函数的地址,再去调用它所指向的函数的时候,被调的那个函数(a函数)就被叫做回调函数。
计算器的模拟实现:
#include
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("**** 1.add 2.sub ****\n"); printf("**** 3.mul 4.div ****\n"); printf("**** 0.exit ****\n"); printf("***********************\n"); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: printf("请输入两个 *** 作数:>");//每个case语句中都要写这两句代码这样就使代码冗 //余 scanf("%d %d", &x,&y); ret = add(x,y); printf("%d\n",ret); break; case 2: printf("请输入两个 *** 作数:>"); scanf("%d %d", &x,&y); ret = sub(x,y); printf("%d\n",ret); break; case 3: printf("请输入两个 *** 作数:>"); scanf("%d %d", &x,&y); ret = mul(x,y); printf("%d\n",ret); break; case 4: printf("请输入两个 *** 作数:>"); scanf("%d %d", &x,&y); ret = div(x,y); printf("%d\n",ret); break; case 0: printf("退出计算器\n"); break: defalut: printf("选择错误\n"); break; } } while (input); }
那么改进这个代码,就现在掌握的知识有两种方法
1、使用函数指针数组来实现
2、使用回调函数
1.2.2、案例二:这里使用回调函数来进行改进
#include
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("**** 1.add 2.sub ****\n"); printf("**** 3.mul 4.div ****\n"); printf("**** 0.exit ****\n"); printf("***********************\n"); } //这里通过重新封装一个函数,来解决代码冗余的问题。 void calc(int (*p)(int,int))//将add、mul、sub、div函数的地址传给calc函数,calc函数使用 //指针p来接收函数地址。 { int x = 0; int y = 0; int ret = 0; scanf("%d %d",&x,&y); ret = p(x,y);//在适当的位置通过函数指针调用加、减、乘、除函数 printf("%d\n",ret); } int main() { int input = 0; int x = 0; int y = 0; int ret = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: calc(add);//这里将add函数(函数名相当于函数地址,这里是传址调用)作为参 //数传给calc函数 break; case 2: calc(sub); break; case 3: calc(mul); break; case 4: calc(div); break; case 0: printf("退出计算器\n"); break: defalut: printf("选择错误\n"); break; } } while (input); } 这样的机制就叫做回调函数的机制
先来回忆一下前面说过的冒泡排序,分析一下冒泡排序中存在的问题。
冒泡排序
#include
//bubble_sort函数只能排序整型数据 void bubble_sort(int arr[],int sz)//写死之后,参数就固定了, { //趟数 int i = 0; for(i = 0;i < sz - 1;i++) { //一趟冒泡排序的过程 int j = 0; for(j = 0;j arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } } } void print(int arr[],int sz) { int i = 0; for(i = 0;i 上述代码的问题是,一但函数写死之后,参数就固定了,就只能排序固定的数据了。
分析如何用qsort函数来解决冒泡排序中存在的问题
1.3、qsort函数1.3.1、qsort函数介绍首先来了解一下qsort函数
qsort函数是C语言标准库提供的排序函数,qsort函数的底层是快速排序的思想。
1.3.2、 qsort函数的使用qsort函数可以排序任意类型的数据
comper是一个指针,指向一个比较函数,这个函数的参数是const void*,const void*
comper的意思为比较
(1)、对qsort函数的分析
base指针指向待排序数组的第一个元素
num表示这个数组有几个待排序的元素
size表示这些待排序的数据的大小,单位(字节)
compar表示指针指向一个函数,这个函数用来比较两个元素(2)、对参数void* base的解读
1>、void*的指针表示为无具体类型的指针,所以void*的指针可以接收任意类型的地址
由于qsort函数可以排序任意类型的数据,所以用void*作为指针base的指针型。
2>、但是 void *p = &i;时,不能直接进行解引用 *** 作,
可以将指针p强制类型转换成与i相应的指针类型来解引用。
例如:
(3)、对参数int(*comper)(const void*,const void*)进行解读
1>、 由于qsort函数可以对任意类型的数据进行排序,
例如:整型、浮点型、字符串、结构体等。
由于每个类型的比较方式不同,所以比较函数由用户自己编写,将函数地址传给 qsort函数,所以用指针comper来接收比较函数的地址
2>、返回类型为int
qsort函数规定:
当第一个指针指向的元素<第二个指针指向的元素,返回一个小于0的数子;
当第一个指针指向的元素=第二个指针指向的元素,返回一个等于0的数子;
当第一个指针指向的元素>第二个指针指向的元素,返回一个大于0的数子;
1、整型类型比较
#include
#include
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int cmp_int(const void* e1, const void* e2)
{
if (*(int*)e1 < *(int*)e2)
return -1;
else if(*(int*)e1 > *(int*)e2)
return 1;
else
return 0;
}
//int cmp_int(const void* e1, const void* e2)
//{
// return(*(int*)e1 - *(int*)e2);
//}可以用这个代码优化前面的比较函数,可以更好的控制排序为降序或升序
int main()
{
int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
print(arr, sz);
return 0;
}
2、结构体排序
1>、按名字排序
#include
#include #include struct Stu { char name[20]; int age; }; //按名字比较 int cmp_stu_by_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } void print(struct Stu* s, int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%s %d\n", s[i].name, s[i].age); } } int main() { struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} }; //按名字比较 int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_name); print(s,sz); return 0; } 这是按照字符串中字母对应的ASCII码值进行比较
1.3.3、用冒泡排序思想模拟实现qsort2>、按年龄比较
#include
#include #include 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; } void print(struct Stu* s, int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%s %d\n", s[i].name, s[i].age); } } int main() { struct Stu s[] = { {"zhangsan", 20},{"lisi", 55},{"wangwu", 40} }; //按名字比较 int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_age); print(s,sz); return 0; } 结果为:
#include
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)//这里用整型数据进行说明,在内存中每个元素占4个字节,
//width=4,一个字节一个字节进行交换,
{
char tmp = *buf1;//这里申请char类型的数据空间,原因是char类型的数据占一个内存空
//间,指针buf指向的内容也占一个字节的内存空间
*buf1 = *buf2;
*buf2 = tmp;
buf1++;
buf2++;
}
}
void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
//一趟冒泡排序的过程
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
//通过指针cmp来调用函数cmp_int,将两个待比较数据的地址传给cmp_int函数
{
Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
//Swap函数用来进行比较后的交换,这里交换是一个字节一个字节来交换数据。
//base的类型是void*,强制类型转换成char*,width是数据的宽度,
}
}
}
}
int cmp_int(const void* e1, const void* e2)
{
return(*(int*)e1 - *(int*)e2);//这里可以改变数组的排序方式
}
void test()
{
int arr[] = { 2,1,3,7,5,9,6,8,0,4 };
int sz = sizeof(arr) / sizeof(arr[10]);
bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
print(arr, sz);
}
int main()
{
test();
return 0;
}
上述代码画图将讲解:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)