目录
1.回调函数的实现
2.qsort的使用
2.1简单了解qsort
2.2使用qsort函数对结构体类型数据进行排序
利用age来进行排序
用姓名来进行排序
3.模拟实现qsrot函数
什么是回调函数,回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条 件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
例如,分析下面代码,case1、case2等,他们都用很多相同的语句,这样过于冗余,那么回调函数可以解决这种冗余。
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(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = arr(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = arr2(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = arr3(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = arr4(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
使用回调函数:
void calc(int (* pf)(int , int)) //传过来的是函数的地址,用函数指针来接收
{
int x, y;
int ret = 0;
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*pf)(x, y);//ret = pf(x, y);
printf("ret = %d\n", ret);
}
int main()
{
//指针的运用,回调函数实现
int input = 1;
do
{
printf("*************************\n");
printf(" 1:arr 2:arr2 \n");
printf(" 3:arr3 4:arr4 \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
calc(arr);//传过去的是函数的地址。
break;
case 2:
calc(arr2);
break;
case 3:
calc(arr3);
break;
case 4:
calc(arr4);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
qsort函数可以实现任意类型数据的排序
qsort( void *p ,num,sz, const void* a, const void* b),qsort含有四个参数
四个参数分别是
第一个是void 类型的指针,用于指向排序的数组首元素的地址,为啥是void呢,那就是要接收任意类型的数据。
第二个是 指针指向的数组的个数,用sizeof来求
第三个是指针指向的数组的类型的字节数。用sizeof,例如sizeof(a[0]).
第四个是函数指针,要提供一个函数可以对数据进行对比的函数。(> 0,= 0,0< )
qsort写法
void qsort (void *base, size_t nitems, size_t size, int (*compar) (const void *, const void*))
int paixu(const void* p1,const void* p2)
{
//在这里最重要的是把数据进行强制转换类型,转换成int类型再进行比较
return *(int*)p1 - *(int*)p2; //这里进行转换并且解引用进行比较
}
int main()
{
int arr[10] = { 5,4,8,1,3,9,0,2,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);//元素个数
int SZ = sizeof(int);//字节大小
qsort(arr, sz, SZ,paixu );
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
总结一下,qsort本质其实就是能接收任意类型的数据并将其排序,内部其实就是将接收到的数据进行强制类型的转换成int,然后对指针解引用进行比较。
使用qsort函数实现结构体数据的排序
用结构体赖比较的话,要搞明白用什么来比较
第一个如果是int类型那么可以直接比较大小 ,例如年龄来比较 可以用 < = >
第二个如果是字符类型那么就要用到库函数strcmp来比较字符串的大小,例如,姓名
#include
#include//qsort头文件
struct stu
{
char name[20];
int age;
};
//用年龄的方法来比较
int cmp_age(const void* p1, const void* p2) //大于则返回>0,等于返回0,小于返回0 <
{
//强制转换类型为结构体指针,这样才可以找到年龄来比较
return ((struct stu*)p1)->age - ((struct stu*)p2)->age; //这里记得要括号起来才可以
//用箭头找到成员age, 结构体指针 -> 结构体成员
//默认升序,如果降序可以反过来
//return ((struct stu*)p2)->age - ((struct stu*)p1)->age;
}
void ax()
{
struct stu arr[] = { {"zhangsan",19},{"lisi",28},{"laowu" ,21} };//初始化
int sz = sizeof(arr) / sizeof(arr[0]);//求元素个数
int SZ = sizeof(arr[0]);//求字节大小
struct stu* p1 = arr; //结构体类型的指针指向结构体类型的arr地址。
qsort(arr, sz, SZ,cmp_age ); //第四个参数是放一个比较的方法函数,年龄来比较
//打印
for (int i = 0; i < sz; i++)
{
printf("%s %d \n", p1[i].name,p1[i].age);
}
}
int main()
{
ax();
return 0;
}
#include
#include//strcmp头文件
#include//qsort头文件
struct stu
{
char name[20];
int age;
};
//用姓名字符串来比较
int cmp_name(const void* p1, const void* p2)
{
//把两个数据放入strcmp函数即可。
//strem 比的是对应位置的ascll码大小,而不是长度
return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}
void ax()
{
struct stu arr[] = { {"zhangsan",19},{"lisi",28},{"laowu" ,21} };//初始化
int sz = sizeof(arr) / sizeof(arr[0]);//求数组元素个数
int SZ = sizeof(arr[0]); //求字节大小
struct stu* p1 = arr; //结构体类型的指针指向结构体类型的arr地址。
qsort(arr, sz, SZ, cmp_name); //姓名比较
for (int i = 0; i < sz; i++)
{
printf("%s %d \n", p1[i].name,p1[i].age);
}
}
int main()
{
ax();
return 0;
}
冒泡排序只能对整数的数据进行排序,而不能做到对任意数据类型进行排序,所以要改造成可以排任意数据类型
使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)。
注意:这⾥第⼀次使⽤ void* 的指针,讲解 void* 的作⽤。
void print_arr(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
//比较相邻数的大小
int cmp1(const void* p1, const void* p2)//这里类型要为int,还要进行判断然后返回去,类似于qsort函数
{
//在这里统一要转换成int类型的指针然后解引用
return *(int *)p1 - *(int *)p2 ;
//如果p1-p2 大于0那么返回 >0
//如果p1-p2 小于于0那么返回 0<
//如果p1-p2 等于0那么返回 =0
}
//进行两个数的交换
void Swap(char *buf1 , char *buf2, size_t zj)
{
//有几个字节那就换几个,因为都统一改成char类型
//所以通过一个一个字节的交换就可以了
//例如int类型,四个字节,需要切成四个字节,然后一个一个字节的交换
int i = 0;
for (i = 0; i < zj; i++)
{
char tmp = *buf1;
*buf1 = *buf2;
*buf2 = tmp;
buf1++;//地址要下一个
buf2++;//这里也是下一个
}
}
void ax(void* base, size_t sz, size_t zj ,int (*cmp) (const void *p1 ,const void *p2) ) //base void类型为了接收任意的数据类型
{
int i = 0;
//基于冒泡排序框架,两个循环不会改变,改变的是条件而已
for (i = 0; i < sz-1; i++)
{
int j = 0;
for (j = 0; j < sz - 1 - i; j++)
{
//判断的话用到类似于qsort的玩法
//这里要实现arr[j] 与arr[j+1]的比较,就是相邻的两个数比较
//(char *)base这个为首地址 ,(char *)base + j*zj 加上后面的j+zj就可以找到第一个了,第二个同理可以找到
if (cmp((char*)base + j * zj, (char*)base + (j + 1) * zj) > 0) //找地址,全部统一强制类型转换成char类型,因为char只有一个字节好操作。
{//如果大于0说明要换位置,下面进行实现交换
Swap((char*)base + j * zj, (char*)base + (j + 1) * zj, zj);//地址也是放这个,因为要想交换数据,必须通过地址来改。
}
}
}
}
int main()
{
//char arr[] = { 'I',' ','a','m',' ','a','p','p','y' };
//char arr[] = ;
int arr[] = { 5,1,4,8,3,7,6,0,9,2 };
int sz = sizeof(arr) / sizeof(arr[0]);
int zj = sizeof(arr[0]);
//size_t为无符号整数,typedef unsigned int size_t;
ax(arr, sz , zj,cmp1);//这里要用到回调函数,第四个参数要放入一个函数指针用于回调使用。
print_arr(arr, sz);
return 0;
}