学习目标:
提示:掌握回调函数,qsort函数
学习内容:
- 指向函数的函数指针
- 回调函数
- qsort函数
关于指针函数
先了解一下指针函数与函数指针的区别
指针函数,落脚点在函数,故它是一个函数,函数的返回值有很多,整型值,字符型值等,当然也可以是指针型.指针函数其返回值为指针。声明形式为:
ret *func(args,...)
其中,func表示一个函数,ages是形参列表(复杂函数一般形参较多),而ret *就是函数func的返回值,是一个指针。
例如
int *p(int x,int y);
a与括号里的结合是函数名,调用他以后,得到一个int *类型的指针,x,y是形参。
因为我们知道int *p是定义一个整形指针变量,int* p是一个表示(int*)的变量。故指针函数就是
类型+*函数(函数参数)。
这里提及一下,主要说函数指针。
函数指针
声明为
ret (*p)(args,...)
函数指针的落脚点在指针,故他表示的是一个指针,可以说这个指针用来表示函数的地址形式,该指针指向一个函数,所以它是指向函数的函数指针。其中 ret为为返回值,*p为指针(指向一个函数),args为形参列表。
我们一般这样定义函数指针 类型+(指针)+函数参数
int (*p)(int ,int);
我们写一个加法函数和减法函数,在定义函数指针用来存放函数。
int Add(int x,int y) { return x + y; } int sub(int x, int y) { return x - y; } int main() { int (*pf)(int, int) = Add; int(*pfArr[4])(int, int) = { Add,sub };//pfArr是一个函数指针数组 int* (*ppfArr[4])(int, int) = {Add, sub};//ppfArr是一个指向函数指针数组的指针变量 return 0; }
其中也有函数指针数组及其指针的表示方法。
回调函数
在函数传参中,传入函数指针类型的参数时,该函数就是一个回调函数。继续用求和函数展示:
//回调函数 void Calc(int(*p)(int, int))//假设传进来的是加减乘除函数 调用的函数即为回调函数 { int x = 0; int y = 0; int ret = 0; printf("请输入两个数"); scanf("%d %d",&x,&y); ret = p(x,y);//p为函数指针 printf("%d", ret); }
qsort函数
qsort函数是一个库函数,它的功能是用来排序的,其中是以升序的方式排序,本质为快速排序。
因为冒泡排序中传入的参数类型只能为整形,不能排序字符,结构体等,故qsort函数被作用来排序各种类型的数据。
//冒泡排序 void bubbil_sort(int arr[], int sz) { //两两比较,升序 int i = 0; for (i = 0; i < sz - 1; i++) { for (int j = 0; j < i; j++) { if (arr[j]>arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } //算法实现的数据类型单一,例如上述函数只能有整形函数
qsort函数其定义
void qsort(void*base,//排序的第一个元素 size_t num,//待排元素个数 size_t size,//每个元素大小,单位字节 int (*cmp)(const void *p1 ,const void *p2))//指向一个函数,这个函数可以比较两个元素大小
qsort函数优点: 直接用就可排序,,,,可以排序任意类型的数据。
其中需要注意的是,因为是任意数据类型,故void* 表示无具体类型地址 ,只是一个表示可以是任意类型,并没有确切类型。 并且关于void *p=&a;"void *类型不能*p(简引用操作)",此时可用强制类型转换数据类型为某一类型。
在上述定义中,int(*cmp)(const void*p1,const void *p2)是指向一个函数,这个函数由你自己来构造,这个函数用来比较这两个元素大小且这两个元素因为是任意类型,排序哪一种确定的类型数据,你所构造的函数就是该类型数据。其他类型需重新构造在调用。
如排序一组整型的数据
int cmp_int(const void*, const void*) //比较两个整形数据 { return *(int*)p1 - *(int*)p2;//强制类型转化 升序排 }//返回值与0比较 test1() { int arr[] = { 9,8,7,6,5,4,3,2,1 }; int sz = sizeof(arr); qsort(arr, sz, sizeof(arr[0], cmp_int);//提供一个比较函数,可比较两个整形函数 } int main() { test1(); return 0; }
若数据类型为结构体
//排序结构体 struct stu { char name[20]; int age; }; int cmp_stu_age(const void* p1,const void* p2)//排序年龄 { return ((struct stu*)p1->age) - ((struct stu*)p2->age); } int cmp_stu_age(const void* p1, const void* p2)//排序名字 { return strcmp(((struct stu*)p1->name) , ((struct stu*)p2->name)); } void test2() { struct stu a[] = { {"zhangsan",20} ,{"lisi",25}, {"wangwu",50} }; int sz = sizeof(a) / sizeof(s[0]); qsort(a,sz,sizeof(s[0]),cmp_stu_age) }
当然因为冒泡排序类型固定,我们可以这样改写使得冒泡排序与qsort函数具有相同的功能。
其中参数就是qsort函数中这样的一个形参。
用void *base表示首元素。size_t num表示元素个数,size_t size为字节大小,cmp为一个比较字符大小的函数指针。
//用冒泡排序写快速排序 void bubble_sort(void *base,size_t num,size_t size,int (*cmp)(const void *p1,const void *p2)) { //确定趟数 int i = 0; size_t size= 0; for (i = 0; i < num - 1; i++) { for (int j = 0; j < num - 1; j++) { if (cmp((char *)base+j*size,(char *)base+j+1*size) > 0) { sawp((char*)base + j * size, (char*)base + j + 1 * size, size); //交换 } } } } void swap(char* p1, char* p2, int size) { int i = 0; for (i = 0; i < size; i ++ ) { char temp =*p1; *p1 =*p2; *p2 = temp;//交换字符 p1++; p2++; } }
这里的冒泡排序中,因为数据类型不确定,但无论是整形,还是结构体里的元素,还是字符型,都可以用字节大小来比较,此时强制类型转换为字符型,乘以相对应自己的字节大小,这样每个数据都可表示出来(char *)base+j*size,