深入理解指针5
1.回调函数是什么
回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条 件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
上一篇博客中写了一个简易的计算器
int Add(int x, int y) { return x + y; } int Sub(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; } void menu() { printf("-------------------------------\n"); printf("----- 1. add 2. sub -----\n"); printf("----- 3. mul 4. div -----\n"); printf("----- 0. exit -----\n"); printf("-------------------------------\n"); } void calc(int (*pf)(int, int)) { int x = 0; int y = 0; int r = 0; printf("请输入2个操作数:"); scanf("%d%d", &x, &y); r = pf(x, y); printf("结果是: %d\n", r); } int main() { int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: calc(Add); break; case 2: calc(Sub); break; case 3: calc(Mul); break; case 4: calc(Div); break; case 0: printf("退出计算器\n"); break; default: printf("输入错误,请输入0~4的值\n"); break; } } while (input); return 0; }这里int (*pf)(int, int)作为calc函数的参数,说白了回调函数就是将函数的地址作为新函数的参数,以达到进行调用的目的,通常可起到简化代码量的作用。
2. qsort
qsort函数原型
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*))qsort是基于快速排序实现,可排序任意类型的数据
1.void*指向数组中第一个要排序的对象,转换为void*。
2.size_t num数组中按基数指向的元素数。
3.size_t size数组中每个元素的字节大小。
4.int (*compar)(const void*,const void*) (函数指针)指向一个比较两个元素的函数。
其中comp函数的规则如下
| 返回值 | 含义 |
| <0 | p1指向的元素小于p2指向的元素 |
| >0 | p1指向的元素大于p2指向的元素 |
| =0 | p1指向的元素等于p2指向的元素 |
由此我们可得到以下代码(需包含stdlib.h )
int comp(const void* p1, const void* p2)//void*无法解引用,需要强制转换为int*类型 { //if (*(int*)p1 < *(int*)p2) // return -1; //else if (*(int*)p1 > *(int*)p2) // return 1; //else // return 0; return(*(int*)p1 - *(int*)p2); } int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int len = sizeof(arr) / sizeof(arr[0]); int size = sizeof(int); qsort(arr,len,size,comp); for (int i = 0; i < 10; i++) { printf("%d ", *(arr + i)); } return 0; }如果想要降序排列
return(*(int*)p2 - *(int*)p1);也可对结构体排序
struct Stu { char name[20]; int age; }; int cmp_age(const void* p1,const void* p2)//年龄升序 { return (*(struct Stu*)p1).age - (*(struct Stu*)p2).age; } int main() { struct Stu arr[] = { {"zhangsan",18},{"lisi",25},{"wangwu",15} }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), cmp_age); for (int i = 0; i < sz; i++) { printf("%d ", arr[i].age); } return 0; }还可通过name进行排序,等后面介绍strcmp函数后再实现。关于结构体其他的知识后面也会写博客介绍
3. qsort函数的模拟实现
使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)
首先给出冒泡排序的基础代码
void bubble_sort(int* arr, int sz) { int flag = 1; for (int i = 0; i < sz - 1; i++) { for (int j = 0; j < sz - i - 1; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = 0; } } if (flag) return; } }仿造sqsort给出函数原型
void my_qsort(void* base, size_t len, size_t size, int(*cmp)(const void* p1, const void* p2))主体还是两个for循环来控制排序的次数
for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - i - 1; j++) { ....... } } }下面是否进行该次排序的条件可通过comp函数实现
int comp(const void* p1, const void* p2) { return *(int*)p1 - *(int*)p2; }冒泡排序的交换可通过swap函数实现
void swap(char* buf1, char* buf2, size_t size) { for (int i = 0; i < size; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } }注意由于这里是char* 类型的指针,所以交换时只能一个字节一个字节的进行交换,看图
完整代码如下
int comp(const void* p1, const void* p2) { return *(int*)p1 - *(int*)p2; } void swap(char* buf1, char* buf2, size_t size) { for (int i = 0; i < size; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void my_qsort(void* base, size_t len, size_t size, int(*cmp)(const void* p1, const void* p2)) { for (int i = 0; i < len - 1; i++) { for (int j = 0; j < len - i - 1; j++) { if (cmp((char*)base + j * size, (char*)base + j * size + 1)) { swap((char*)base + j * size, (char*)base + (j + 1) * size, size); } } } }这段代码只是为了帮我们理解qsort函数,实际操作直接使用qsort函数即可
