一. 函数指针的两种用途
1.1 回调函数
回调函数就是一个通过函数指针调用的函数
回调函数三要素
- 定义一个回调函数
- 将回调函数的指针注册给调用者
- 当特定的事件或条件发生时,调用者使用函数指针调用回调函数
回调函数的意义在于实现模块间的解耦,模块化编程
应用程序常会调用库函数,但有些库函数却要求应用程序先传给它一个函数,好在合适的时候调用,以完成目标任务。那个被传入,后又被调用的函数就是回调函数
举例:有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法,可以是打电话,敲门或者睡懒觉不要被叫醒。这个叫醒服务是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客自己决定的,也就是回调函数。旅客告诉旅馆怎么叫醒自己,对应把回调函数传入库函数的动作。
#include <stdio.h>
typedef void (*callway)(char*);
void knockDoor(char* name)
{
printf("I'll Knock at a door... for %s\n", name);
}
void callUp(char* name)
{
printf("I'll Call up... for %s\n", name);
}
void overLook(char* name)
{
printf("I'll Overlook... for %s\n", name);
}
void callBed(char* name, callway call_way)
{
printf("Day breaks!!! ");
call_way(name);
}
int main(void)
{
char *customs[3] = {"alice", "Jack", "Eish"};
callway customs_call_way[3] = {knockDoor, callUp, overLook};
for(int i = 0; i < 3; ++i)
{
callBed(customs[i], customs_call_way[i]);
printf("===========\n");
}
return 0;
}
1.2 转移表
转移表实际就是一个函数指针数组,也称分支表
用于将程序控制转移到程序另一部分。关于跳转到程序另一部分最常用的方法是使用 switch 语句,但是使用 switch 语句存在的一个弊端就是如果分支过多,会造成程序的冗长,而转移表刚好能解决这个问题,对于优化程序的结构有很大的帮助
#include <stdio.h>
enum{
add,
sub,
mul,
div,
};
double addNum(double num_1, double num_2){
return num_1 + num_2;
}
double subNum(double num_1, double num_2){
return num_1 - num_2;
}
double mulNum(double num_1, double num_2){
return num_1 * num_2;
}
double divNum(double num_1, double num_2){
return num_1 / num_2;
}
int main(void){
double result = 0;
int operator = 0;
double num_1 = 0;
double num_2 = 0;
printf("Please input two numbers: "); scanf("%lf, %lf", &num_1, &num_2);
printf("Please input operator: +[0], -[1], *[2], /[3] "); scanf("%d", &operator);
switch(operator){
case add:
result = addNum(num_1, num_2);
break;
case sub:
result = subNum(num_1, num_2);
break;
case mul:
result = mulNum(num_1, num_2);
break;
case div:
result = divNum(num_1, num_2);
break;
default:
printf("without this operator\n");
break;
}
printf("result = %lf\n", result);
return 0;
}
#include <stdio.h>
double addNum(double num_1, double num_2){
return num_1 + num_2;
}
double subNum(double num_1, double num_2){
return num_1 - num_2;
}
double mulNum(double num_1, double num_2){
return num_1 * num_2;
}
double divNum(double num_1, double num_2){
return num_1 / num_2;
}
int main(void){
double (*transfer[4])(double, double) = {addNum, subNum, mulNum, divNum};
int operator = 0;
double num_1 = 0;
double num_2 = 0;
printf("Please input two numbers: "); scanf("%lf, %lf", &num_1, &num_2);
printf("Please input operator: +[0], -[1], *[2], /[3] "); scanf("%d", &operator);
printf("result = %lf\n", transfer[operator](num_1, num_2));
return 0;
}
二. qsort 函数
qsort 函数是 C 库中实现的快速排序算法,包含在 stdlib.h 头文件中
函数原型如下
void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *));
-
第一个参数是需要排序的数组的基地址,因为是 void * 类型,所以此函数可以给任何类型的数组进行排序
-
第二个参数是待排序数组有多少个元素
-
第三个参数是单个数组元素的大小
-
第四个参数就是回调函数,一个指向函数的指针,其作用是规定排序规则
回调函数的返回值大于 0 时,交换第一个参数和第二个参数的顺序,回调函数的形参是指向数组元素的指针,强转要转成正确的类型,否则会发生奇怪的事情
int 数据类型的排序
#include <stdio.h>
#include <stdlib.h>
// 从小到大
int compar(const void *elem_1, const void *elem_2){
return *(const int*)elem_1 - *(const int *)elem_2;
}
void printArr(int *arr, int size){
for(int i = 0; i < size; ++i){
printf("%d ", arr[i]);
}
printf("\n");
}
int main(void){
int arr[6] = {-5, 2, 3, -6, 1};
printArr(arr, 6);
qsort(arr, 6, sizeof(int), compar);
printArr(arr, 6);
return 0;
}
结构体数据类型的排序
#include <stdio.h>
#include <stdlib.h>
typedef struct{
char name[8];
int score;
}Student;
// 根据学生成绩从小到大排列
int compar(const void *elem_1, const void *elem_2){
return (**(const Student **)elem_1).score - (**(const Student **)elem_2).score;
}
int main(void){
Student student_1 = {"Alice", 100};
Student student_2 = {"Jack", 99};
Student student_3 = {"Eish", 98};
Student *class[3] = {&student_1, &student_2, &student_3};
for(int i = 0; i < 3; ++i){
printf("%d ", class[i]->score);
}
printf("\n");
qsort(class, 3, sizeof(Student *), compar);
for(int i = 0; i < 3; ++i){
printf("%d ", class[i]->score);
}
printf("\n");
return 0;
}
二维数组排序
#include <stdio.h>
#include <stdlib.h>
typedef struct{
char name[8];
int score;
}Student;
// 根据每个一维数组的第一个元素由大到小排列
int compar(const void *elem_1, const void *elem_2){
return (*(const int **)elem_2)[0] - (*(const int **)elem_1)[0];
}
void printArr(int **arr, int row, int col){
for(int i = 0; i < row; ++i){
for(int j = 0; j < col; ++j){
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main(void){
int arr_create[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int *arr[3] = {arr_create[0], arr_create[1], arr_create[2]};
printArr(arr, 3, 3);
printf("----------------------------\n");
qsort(arr, 3, 8, compar);
printArr(arr, 3, 3);
return 0;
}
作业
1. 编写函数,给定一个数组 array 和一个值 val,,删除所有数值等于 val 的元素,并返回移除后数组的新长度。
数组在内存空间的地址是连续的,所以在删除元素的时候,是通过移动其它元素,去覆盖要删除的元素,并不是让待删除元素凭空消失
示例1
输入:array = [3, 2, 2, 3],val = 3
输出:2
解释:删除后 array 的前 2 个元素均为 2,无需考虑数组中超出新长度后面的元素
示例2
输入:array = [0, 1, 2, 2, 3, 2],val = 2
输出:3
解释:删除后 array 的前 3 个元素为 0,1,3
2. 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。