C语言第17讲
回调函数
回调函数就是⼀个通过函数指针调⽤的函数
当我们想写一个简易的计算机系统的时,会和先前写 扫雷 猜数字游戏 一样的模版
第一版 代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void print()
{printf("########################\n");printf("### 1.Add 2.Sum ###\n");printf("### 3.Mul 4.Div ###\n");printf("### 0.Exit ###\n");printf("########################\n");
}int Add(int x, int y)
{return x + y;
}int Sum(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;
}int main()
{int input = 0;do{print();printf("请选择:");scanf("%d", &input);int x = 0;int y = 0;int ret = 0;switch (input){case 1:printf("请输入两数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("%d\n", ret);break;case 2:printf("请输入两数:");scanf("%d %d", &x, &y);ret = Sum(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;default:printf("选择错误,请从选择\n");break;}} while (input);return 0;
}
我们会发现发现在whitch 语句中的选择会有太多重复我进行第二版修改
第二版 代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void print()
{printf("########################\n");printf("### 1.Add 2.Sum ###\n");printf("### 3.Mul 4.Div ###\n");printf("### 0.Exit ###\n");printf("########################\n");
}int Add(int x, int y)
{return x + y;
}int Sum(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;
}int clac(int (*p_fun)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两数:");scanf("%d %d", &x, &y);ret = p_fun(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{print();printf("请选择:");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sum);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出成功\n");break;default:printf("选择错误,请从选择\n");break;}} while (input);return 0;
}
对比第一版,第二版,将重复的内容分装成了个函数,重复内容中,不同的是每次函数的调用,我们便将所需的函数指针传过去。
在我们分装的函数中,调用其他函数的方式便是函数回调(利用函数的地址去调用它)
我们也可以利用 函数指针数组 去修改第一版代码
第三版 代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void print()
{printf("########################\n");printf("### 1.Add 2.Sum ###\n");printf("### 3.Mul 4.Div ###\n");printf("########################\n");
}int Add(int x, int y)
{return x + y;
}int Sum(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;
}int main()
{int(*arr_fun[])(int, int) = {0,Add, Sum, Mul, Div};int input = 0;do{print();printf("请选择:");scanf("%d", &input);int x = 0;int y = 0;int ret = 0;printf("请输入两数:");scanf("%d %d", &x, &y);ret = arr_fun[input](x,y);printf("%d\n", ret);} while (input);return 0;
}
第二版 修改的为 switch语言 重复内容,与第一版的内容没变
第三版 修改的 switch语句 ,导致内容会有缺少,缺少在选择退出 和选择错误的程序
qsort函数
在 qsort 先拿出冒牌排序
冒泡排序 代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void bubble_sort(int* p, int sz)
{for (int i = 0; i < sz - 1; i++){for (int j = 0; j < sz - 1 - i; j++){if (p[j] > p[j + 1]){int tmp = p[j];p[j] = p[j + 1];p[j + 1] = tmp;}}}
}int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}
由此提出个问题:怎样排序才能不受限于类型
qsort 就是个排序功能库函数,底层是快速排序逻辑,不受限排列类型
qsort 函数的使用
qsort 函数声明:
void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *));
base 为待排序数组的开始地址
num 为待排序数组的所有元素总数
size 为元素所占内存大小
compar 为比较函数 要求传输比较函数的指针 类型为 int (const void*, const void*);
在qsort 函数中需要返回值,只有返回值大于0时,才会进行替换
例1:利用sqort 排序升序整形数组
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // qsort 头文件int cmp_int(const void* p1, const void* p2) // 待比较的两指针
{// 由于void* 类型不能直接解引用,不能+ - 整数return (*(int *)p1) - (*(int *)p2); // 可以将其装换为可以进行 运算类型
}int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]),cmp_int); // 填入函数指针for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}
对比函数 已在 要比较两元素的开始
将 p1 装换为准确类型是为了知道一个元素的所占内存大小
例2:利用qsort 排序结构体
2.1:按名字排列结构体
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct stu
{char name[20];int age;
};int cmp_str(const void* p1, const void* p2)
{return strcmp(((struct stu*)p1)->name, ((struct stu*)p2)->name);
}int main()
{struct stu s[] = {{"zhangsan",20},{"lisi",35},{"wangwu",18}};int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_str);return 0;
}
strcmp函数排序方式:(这个函数只会 两两比较,下面是这种方式操作下的举例)
abc ,cde ,acd 先拿出第一个字符进行比较(底层是利用ASCII表)
a 为 0x97 c为0x99
c比a大 c最后
两个未出先后的取出第二个字符
b和c c大于b
最后排序:
abc acd cde
对比函数 两指针指向的整个结构体,需要细化至结构体中的name
使用((struct stu*)p1)后加 ->name
在使用->时格式:指针 -> 成员名
在使用 . 时格式:结构体变量名称 . 成员名
运行前:
运行后:
2.1:结构体按年龄的比较
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct stu
{char name[20];int age;
};int cmp_str(const void* p1, const void* p2)
{return (((struct stu*)p1)->age) - (((struct stu*)p2)->age);
}int main()
{struct stu s[] = {{"zhangsan",20},{"lisi",35},{"wangwu",18}};int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_str);return 0;
}
运行前:
运行后:
模仿qsort来实现一个冒泡排序函数
对比 qsort 函数 冒泡排序,需修改的地方在
1.函数参数不能被类型固定
2.交换程序不能被类型固定
3.比较因为个性化太强,利用回调函数,让其他人自己写
总结,模型qsort函数写的功能是,基于冒泡排序的交换模块
由原代码的替换模块入手,1 字节 1字节的替换能覆盖所有内存,其余 大小总会有遗留的整数所以确认为(char)
确认元素类型后,需要确认 指针指向位置的准确。
因为需要修改为无固定类型排序,声明已经改为void *p
且确定由1 字节 1字节方式替换
所以我们需要一个元素的内存大小
才能知道两个交换元素的地址
函数声明参数修改如下:
(void* p_arr, size_t num, size_t size)
p_arr 待排序数组开始地址
nim 待排序的所有元素个数
size 待排序元素的大小
冒泡排序 替换模块如下:
for (int i = 0; i < num - 1; i++)
{for (int j = 0; j < num - 1; j++){if (>0){for (int m = 0; m < size; m++){char tmp = *((char*)p_arr + j * size+m);*((char*)p_arr + j * size+m) = *((char*)p_arr + (j + 1) * size+m);*((char*)p_arr + (j + 1) * size+m) = tmp;}}}
}
if 语句为判断模块,判断为>0进行替换操作
比较模块 由于个性化太强,利用回调函数
我们已知进行比较两个元素的地址
由上我们可以知道判断模块的类型:
1.返回值为 int
2.传参两个指针
比较函数声明:
int bubsort_cmp(const void*p1, const void*p2);
函数代码如下:
int bubsort_cmp(const void*p1, const void*p2)
{return (*(int*)p1) - (*(int*)p2);
}void bubble_sort(void* p_arr, size_t num, size_t size, int(*cmp)(const void*, const void*))
{for (int i = 0; i < num - 1; i++){for (int j = 0; j < num - 1; j++){if (cmp((char*)p_arr +j*size, (char*)p_arr +(j+1)*size) > 0){for (int m = 0; m < size; m++){char tmp = *((char*)p_arr + j * size+m);*((char*)p_arr + j * size+m) = *((char*)p_arr + (j + 1) * size+m);*((char*)p_arr + (j + 1) * size+m) = tmp;}}}}
}