文章目录
- 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*是结构体指针类型,指针名是ps
printf("%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;
}