【C语言】指针的进阶(二)—— 回调函数的讲解以及qsort函数的使用方式
温馨提示:这篇文章已超过445天没有更新,请注意相关的内容是否还可用!
目录
1、函数指针数组
1.1、函数指针数组是什么?
1.2、函数指针数组的用途:转移表
2、扩展:指向函数指针的数组的指针
3、回调函数
3.1、回调函数介绍
3.2、回调函数的案例:qsort函数
3.2.1、回顾冒泡排序
3.2.1、什么是qsort函数?
1、函数指针数组
1.1、函数指针数组是什么?
函数指针数组是什么?首先主语是数组,数组是一个存放相同类型数据的存储空间。那我们已经学习了指针数组,比如:
char* arr[5] ———— 字符指针数组,它是一个数组,存放的是字符指针。
int* arr[5] ———— 整型指针数组,它是一个数组,存放的是整型指针。
假设有这么一个使用场景,我需要将几个函数的地址存放到一个数组中,那应该怎么存?下面给大家介绍一下:函数指针数组
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int main()
{
int (*pf1)(int, int) = &Add; //pf1和pf2是函数指针
int (*pf2)(int, int) = ⋐
//数组中存放类型相同的多个数组
int (*pfArr[4])(int, int) = { &Add,&Sub }; //pfArr就是函数指针数组
return 0;
}
函数指针数组的写法与函数指针非常相似,只需要在名字后加个方括号[ ]就可以了。
注意:因为数组是一个存放相同类型数据的存储空间,所以函数指针数组只能够存放返回类型和参数类型都一致的函数的函数地址。
1.2、函数指针数组的用途:转移表
用C语言实现一个计算器功能(加减乘除):
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 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 a = 0;
int b = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入2个操作数:");
scanf("%d %d", &a, &b);
ret = add(a, b);
printf("%d\n", ret);
break;
case 2:
printf("请输入2个操作数:");
scanf("%d %d", &a, &b);
ret = sub(a, b);
printf("%d\n", ret);
break;
case 3:
printf("请输入2个操作数:");
scanf("%d %d", &a, &b);
ret = mul(a, b);
printf("%d\n", ret);
break;
case 4:
printf("请输入2个操作数:");
scanf("%d %d", &a, &b);
ret = div(a, b);
printf("%d\n", ret);
break;
case 0:
printf("退出计算器\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
上面的代码虽然能实现一个计算器功能,但是可以发现,这个代码特别地冗余,重复的部分非常多,并且如果需要添加多一个功能是,又需要再添加多一个case,导致代码越来越长,重复部分也越来越多,这是非常不好的代码习惯,那有什么办法能够解决呢?
其实我们通过观察可以发现,这些函数有一些特点,就是除了函数名不同之外,返回类型以及参数类型都是一致的。
既然除了函数名不同之外其余都相同,那么是否就可以使用函数指针数组来改造一下代码?
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 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 a = 0;
int b = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
//创建一个函数指针数组
int (*pfArr[])(int, int) = { NULL,add,sub,mul,div };
//为了使数组下标与菜单序号对应起来,在0下标处放置一个NULL
if (input == 0)
{
printf("退出计算器\n");
}
else if (input >= 1 && input arr[j + 1])
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
int main()
{
int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i = 0;
for ( i = 0; i
上面就是冒泡排序的实现,但是可以看到,这个冒泡排序其实是有缺陷的,它的参数是int类型,限制了它只能够排序整型数据!而这里即将讲到的qsort函数就是一个可以用来排序任意类型数据的函数。
3.2.1、什么是qsort函数?
qsort是一个库函数,底层使用的是快速排序的方式对数据进行排序。头文件:
这个函数可以直接使用用来排序任意类型的数据。
当然除了快速排序,还有很多排序,例如:冒泡排序、选择排序,希尔排序,归并排序等等
qsort函数定义原型:
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
- void* base:待排序数组的第一个元素的地址
- size_t num:待排序数组的元素个数
- size_t size:以字节为单位,待排序数组中一个元素的大小。
- int (*compar)(const void*,const void*):函数指针,指向一个函数,用来比较两个元素,由用户自行创建并封装。
比较函数的形参中为什么用的是void*:
void* 是无具体类型的指针,不能进行解引用操作符,也不能进行+-整数的操作,它是用来存放任意类型数据的地址(可以理解为垃圾桶,什么都能装,当需要用时再强制类型转换为需要的类型)。只有void*被允许存放任意类型数据的地址,如果是其他类型的指针编译器会报错。正是因为定义qsort函数时用的是void*,qsort函数才可以排序任意类型的数据。
使用qsort函数最重要的就是最后一个参数,这个参数决定了qsort函数比较两个元素的规则。这里先写一个用于排序整型数据的比较函数cmp_int:
int cmp_int(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; }比较函数的要求:
- 当p1指向的元素大于p2指向的元素时,返回大于0的数
- 当p1指向的元素等于p2指向的元素时,返回0
- 当p1指向的元素小于p2指向的元素时,返回小于0的数
【完整代码】
使用qsort函数排序整型数据。
int cmp_int(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } int main() { int arr[] = { 10,9,8,7,6,5,4,3,2,1 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_int); int i = 0; for ( i = 0; i同理,qsort函数排序结构体类型数据(下面例子以结构体中的年龄来排序):
struct Stu { char name[20]; int age; }; int cmp_struct(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } int main() { struct Stu arr[] = { {"zhangsan",20},{"lisi",21},{"wangwu",22} }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_struct); return 0; }【运行结果】
可以发现确实是完成了按年龄排序。
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!
如果觉得作者写的不错,求给博主一个大大的点赞支持一下,你们的支持是我更新的最大动力!




