文章目录
- 1.回调函数是什么?
- 2.qsort 使⽤举例
- 2.1 使⽤qsort函数排序整型数据
- 2.2 使⽤qsort排序结构数据
- 3.qsort函数的模拟实现
1.回调函数是什么?
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
使用回调函数改造前:
#include <stdio.h>
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;
}
/*****************************************************main函数******************************************************/
int main(){int x, y;int input = 1;int ret = 0;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);switch (input){case 1:printf("输入操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输入操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输入操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输入操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
使用回调函数改造后:
#include <stdio.h>
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;
}/*****************************************************calc函数******************************************************/
void calc(int(*pf)(int, int)){int ret = 0;int x, y;printf("输入操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}/*****************************************************main函数******************************************************/
int main(){int input = 1;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf("*************************\n");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("选择错误\n");break;}} while (input);return 0;
}
2.qsort 使⽤举例
2.1 使⽤qsort函数排序整型数据
qsort
是C语言中的一个库函数。
使用qsort
函数要包含一个头文件:#include <stdlib.h>
这个函数是用来对数据进行排序的,对任意类型的数据都能进行排序。
就比方下面的冒泡排序:
void bubble_sort(int arr[], int sz) {//趟数int i = 0;for (i = 0; i < sz - 1; i++) {//一趟内部的两两比较int j = 0;for (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;}}}
}
这个程序排序整型数据是没问题的,但是能排序字符数组吗?能排序字符串吗?能排序浮点数吗?能排序结构体吗?
这个排序算法只能排序整型。
我们来看一下
qsort
函数的定义:void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));
我们把这个代码划分一下:
void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*)//函数指针,传递函数的地址);
void* base
:指向待排序数组的第一个元素的指针。
size_t num
:base指向数组元素中元素的个数。
size_t size
:base指向的数组元素中一个元素的大小,单位是字节。
int (*compar)(const void*,const void*)
:函数指针,传递函数的地址
字符串的大小比较大小不能使用>
,>=
,<
,<=
,==
,!=
。
应该使用strcmp
函数。
比较两个结构体呢?
也不能用>
,>=
,<
,<=
,==
,!=
比吧?
qsort
函数能够实现排序,是因为它很聪明。
既然不同元素代码的比较不同,那么就把他抽离出来。谁要调用这个qsort
函数进行比较,那么谁就来提供比较函数。
这也就是为什么会有个函数指针的原因。
#include <stdio.h>
#include <stdlib.h>
/*
cmp_int函数对函数的返回值有要求
p1指向的元素比p2指向的元素小的时候,返回一个小于0的数字
p1指向的元素和p2指向的元素一样的时候,返回一个等于0的数字
p1指向的元素比p2指向的元素大的时候,返回一个大于0的数字
*/
int cmp_int(const void* p1, const void* p2) {if (*(int*)p1 > *(int*)p2) {// 将指针 p1 和 p2 强制转换为指向 int 的指针,然后通过解引用获取指针指向的整数值return 1;}else if (*(int*)p1 < *(int*)p2) {return -1;}else {return 0;}
}
//当然上面也可以直接返回:
//return *(int*)p1 - *(int*)p2;void print_arr(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}void test1() {int arr[] = { 3,1,7,9,4,2,6,5,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main() {test1();return 0;
}
打印:
0 1 2 3 4 5 6 7 8 9
2.2 使⽤qsort排序结构数据
我们先看一下打印结构体的操作:
struct Stu {char name[20];int age;
};/*结构体成员访问操作符:
. :结构体变量.成员名
->:结构体指针->成员名
*/
int main() {struct Stu s = { "zhangsan",20 };printf("%s %d\n", s.name, s.age);struct Stu* ps = &s;//struct Stu*是结构体指针类型,指针名是psprintf("%s %d\n", (*ps).name, (*ps).age);printf("%s %d\n", ps->name, ps->age);return 0;
}
打印:
zhangsan 20
zhangsan 20
zhangsan 20
下面的代码中有关于strcmp函数的使用,如果不记得的可以看之前的这篇博客:
【C语言】strcmp函数讲解
通过qsort函数,按照名字比较结构体数据大小:
#include <string.h>
#include <stdlib.h>struct Stu {char name[20];int age;
};//按照名字比较两个结构体数据
//名字是字符串,字符串比较是用strcmp函数的
int cmp_stu_by_name(const void* p1, const void* p2) {return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}//测试qsort函数来排序结构体数据
void test2() {struct Stu arr[] = { {"zhangsan",20},{"list",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main() {test2();return 0;
}
通过qsort函数,按照年龄比较结构体数据大小:
#include <string.h>
#include <stdlib.h>struct Stu {char name[20];int age;
};//按照年龄比较两个结构体数据
int cmp_stu_by_age(const void* p1, const void* p2) {return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}//想逆序的话就这么写:
//return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
//其实就是把p1和p2换个位置。void test3() {struct Stu arr[] = { {"zhangsan",20},{"list",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main() {test3();return 0;
}
3.qsort函数的模拟实现
刚刚我们一开始用的是冒泡排序,那么我们能不能把冒泡排序改造一下呢?改造成和qsort
函数一样,可以接受任意类型的数据呢?
这里
(char*)base+4
就是加了4个字节。也就是一个数组位。因为这里是整型数组,一位4字节。如果不是整形数组,那就是加了
width
个字节。从9到8是:
(char*)base+0
到(char*)base+4
从8到7是:
(char*)base+4
到(char*)base+8
中间差的就是位数乘上
width
。
(改版)冒泡排序测试整型:
#include <stdlib.h>int cmp_int(const void* p1, const void* p2) {if (*(int*)p1 > *(int*)p2) {// 将指针 p1 和 p2 强制转换为指向 int 的指针,然后通过解引用获取指针指向的整数值return 1;}else if (*(int*)p1 < *(int*)p2) {return -1;}else {return 0;}
}void print_arr(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++) {printf("%d ", arr[i]);}
}void Swap(char* buf1, char* buf2, size_t width) {int i = 0;char tmp = 0;for (i = 0; i < width; i++) {tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*compar)(const void* p1, const void* p2)) {//趟数int i = 0;for (i = 0; i < sz - 1; i++) {//一趟内部的两两比较int j = 0;for (j = 0; j < sz - i - 1; j++) {//比较两个元素if (compar((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test4() {int arr[] = { 3,1,7,9,4,2,6,5,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main() {test4();return 0;
}
打印:
0 1 2 3 4 5 6 7 8 9
(改版)冒泡排序测试结构体:
比较名字:
struct Stu {char name[20];int age;
};//按照名字比较两个结构体数据
//名字是字符串,字符串比较是用strcmp函数的
int cmp_stu_by_name(const void* p1, const void* p2) {return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}void Swap(char* buf1, char* buf2, size_t width) {int i = 0;char tmp = 0;for (i = 0; i < width; i++) {tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*compar)(const void* p1, const void* p2)) {//趟数int i = 0;for (i = 0; i < sz - 1; i++) {//一趟内部的两两比较int j = 0;for (j = 0; j < sz - i - 1; j++) {//比较两个元素if (compar((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}//测试qsort函数来排序结构体数据
void test5() {struct Stu arr[] = { {"zhangsan",20},{"list",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main() {test5();return 0;
}
比较年龄:
struct Stu {char name[20];int age;
};//按照年龄比较两个结构体数据
int cmp_stu_by_age(const void* p1, const void* p2) {return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}//想逆序的话就这么写:
//return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
//其实就是把p1和p2换个位置。void Swap(char* buf1, char* buf2, size_t width) {int i = 0;char tmp = 0;for (i = 0; i < width; i++) {tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*compar)(const void* p1, const void* p2)) {//趟数int i = 0;for (i = 0; i < sz - 1; i++) {//一趟内部的两两比较int j = 0;for (j = 0; j < sz - i - 1; j++) {//比较两个元素if (compar((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test6() {struct Stu arr[] = { {"zhangsan",20},{"list",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}int main() {test6();return 0;
}