简单不先于复杂,而是在复杂之后。
目录
1. 回调函数
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
1.1 qsort 函数的使用
首先演示一下 qsort 函数的使用
在之前我们写过冒泡排序,但我们只能排序整形数据;
代码如下:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void bubble_sort(int arr[], int sz) { int i = 0; //趟数 for (i = 0; i < sz - 1; i++) { int flag = 1;//假设数组是排好序的 //一趟冒泡排序 int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; flag = 0; } } if (flag == 1) { break; } } } int main() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; //把数组排成升序 int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
我们想使用 qsort 函数,把需要传的参数依次输入后发现,我们需要自己写一个比较函数,比如我们想要比较两个整形,写出 cmp_int 函数,参数是 qsort 要求的(const void* e1, const void* e2), qsort函数对比较函数的返回值也有规定:
把函数内容写完之后,发现 e1、e2 是 void* 的指针,是不能解引用的。
当我们写出这样的代码,发现用 char* 的指针来接收 int型变量的地址是不行的。
如果我们这样写:
我们要想正确写出比较函数,那就可以将 e1、e2 强制类型转换成 int* 类型的指针:
我们修改过后的代码过程还是比较繁琐,不妨这样写:
qsort 函数默认是把降序排成升序
如果我们想把升序排成降序,只需要把比较函数中 e1 和 e2 的位置调换一下就好,逻辑相反,就可以排成降序。
不要忘记使用 qsort 需要引头文件
#include<stdlib.h>
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> int cmp_int(const void* e1, const void* e2) { return (*(int*)e1 - *(int*)e2); } int main() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; //把数组排成升序 int sz = sizeof(arr) / sizeof(arr[0]); //bubble_sort(arr, sz); qsort(arr, sz, sizeof(arr[0]), cmp_int); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
1.2 qsort 排序结构体类型
我们当然也可以使用 qsort 排序其他类型的数据,比如结构体类型:
假设我们按照名字来排序
同理,我们也可以按照年龄来排序:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<string.h> int cmp_int(const void* e1, const void* e2) { return (*(int*)e1 - *(int*)e2); } struct Stu { char name[20]; int age; }; int cmp_stu_by_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } int cmp_stu_by_age(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } void test_2() { //测试使用 qsort 排序结构体数据 struct Stu s[] = { {"zhangsan",15},{"lisi",30},{"wangwu",25} }; int sz = sizeof(s) / sizeof(s[0]); qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);//假设依据名字来排序 qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);//假设依据年龄来排序 } int main() { test_1();//排序整型 test_2();//排序结构体数据 return 0; } void test_1() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; //把数组排成升序 int sz = sizeof(arr) / sizeof(arr[0]); //bubble_sort(arr, sz); qsort(arr, sz, sizeof(arr[0]), cmp_int); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } }
1.3 回调函数模拟实现 qsort(排序整型)
学会了使用 qsort 函数,那么我们也可以使用回调函数,模拟实现 qsort (采用冒泡的方式)
cmp 函数的参数不能简单地写成 (base + j, base + j +1)
因为我们并不知道要比较的数据是哪种类型,所以要改写成这样:
下面是完整的使用回调函数,模拟实现 qsort (采用冒泡的方式)的代码:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<string.h> int cmp_int(const void* e1, const void* e2) { return (*(int*)e1 - *(int*)e2); } void Swap(char* buf1, char* buf2, int width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2)) { int i = 0; //趟数 for (i = 0; i < sz - 1; i++) { int flag = 1;//假设数组是排好序的 //一趟冒泡排序 int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { //交换 Swap((char*)base + j * width, (char*)base + (j + 1) * width, width); flag = -1; } } if (flag == 1) { break; } } } void test_3() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; //把数组排成升序 int sz = sizeof(arr) / sizeof(arr[0]); bubble_sort(arr, sz, sizeof(arr[0]),cmp_int ); int i = 0; for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } } int main() { //test_1();//排序整型 //test_2();//排序结构体数据 test_3(); return 0; }
1.4 回调函数模拟实现 qsort(排序结构体类型)
我们使用回调函数,模拟实现 qsort (采用冒泡的方式)排序了整型的数据,当然也可以排序结构体类型的,只需要稍作修改:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<string.h> struct Stu { char name[20]; int age; }; int cmp_stu_by_name(const void* e1, const void* e2) { return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name); } int cmp_stu_by_age(const void* e1, const void* e2) { return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age; } void Swap(char* buf1, char* buf2, int width) { int i = 0; for (i = 0; i < width; i++) { char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; } } void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2)) { int i = 0; //趟数 for (i = 0; i < sz - 1; i++) { int flag = 1;//假设数组是排好序的 //一趟冒泡排序 int j = 0; for (j = 0; j < sz - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { //交换 Swap((char*)base + j * width, (char*)base + (j + 1) * width, width); flag = -1; } } if (flag == 1) { break; } } } void test_4() { //测试使用 qsort 排序结构体数据 struct Stu s[] = { {"zhangsan",15},{"lisi",30},{"wangwu",25} }; int sz = sizeof(s) / sizeof(s[0]); bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age); } int main() { //test_1();//排序整型 //test_2();//排序结构体数据 //test_3(); test_4(); return 0; }
同理,上面是按照年龄来排序,如果想按照名字排序,修改一下比较函数就可以了。