🍁博客主页:江池俊的博客
💫收录专栏:C语言进阶之路
💡代码仓库:江池俊的代码仓库
🎪我的社区:GeekHub
🎉欢迎大家点赞👍评论📝收藏⭐
前言
回调函数和
qsort
是 C语言编程中重要的概念,它们为我们提供了强大的工具,用于处理函数指针和数组排序。本篇博客将逐步介绍回调函数的概念,详细解释qsort
函数的用法,并通过一个模拟实现,帮助初学者更好地理解这些概念。如果大家不知道函数指针是说明或还不清楚函数指针的内容,可以移步我这篇文章《掌握指针进阶:一篇带你玩转函数指针、函数指针数组及指向函数指针数组的指针!!》
一、什么是回调函数?
回调函数是一种通过函数指针传递给其他函数,并由其他函数在适当时候调用的函数。回调函数的存在使得我们能够将某种特定的行为(代码逻辑)作为参数传递给另一个函数。这在编程中非常有用,因为它允许我们以灵活的方式自定义函数的行为。
📌使用回调函数的优势
- 代码重用: 可以将通用的操作封装在回调函数中,以供多个函数重复使用。
- 灵活性: 回调函数允许我们在运行时动态地指定要执行的代码,从而实现更高度的灵活性。
- 解耦合: 使用回调函数可以将代码分解成独立的模块,减少模块之间的耦合,提高代码的可维护性。
二、qsort
函数及其用法
qsort
是 C 标准库中提供的用于数组排序的函数,它接受一个 比较函数
作为参数,用于确定数组元素的顺序。(这个比较函数是使用者根据自己的需要设计的,因此qsort函数可以实现对任意类型数据的排序)qsort
函数的原型如下:
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
其中,base
是要排序的数组的指针;num
是数组中元素的数量;size
是每个元素的大小,以字节为单位;compar
是用于比较两个元素的函数指针。
这里我们可以通过cplusplus网来查询这个函数的使用方法,也可以使用菜鸟教程网来查询。
📌qsort函数作用
📌qsort函数4个参数的介绍
📌为什么qsort函数的参数是这四个?
qsort
函数之所以有这四个参数,是为了实现通用、灵活且可定制的排序功能。
这些参数的设计和使用有以下几个目的:
- 通用性: 由于
qsort
需要适应不同类型的数据,它通过base
参数接受数组的指针,并使用size
参数来了解每个元素的大小,从而使得排序操作可以应用于各种不同类型的数组。- 灵活性: 通过传递比较函数的指针作为
compar
参数,我们可以在不同的排序场景中定义不同的比较逻辑。这使得我们可以根据需要实现升序、降序或自定义的排序规则。
可定制性:qsort
的设计允许我们在排序过程中自定义元素的比较方式。我们可以根据实际需求提供不同的比较函数,从而实现不同的排序需求。
高效性:qsort
内部使用一种高效的排序算法(通常是快速排序的变种),以确保在大多数情况下能够高效地完成排序操作。
综上所述,这四个参数的设计使得 qsort
函数成为一个强大且通用的排序工具,可以适应不同类型的数据、实现不同的排序规则,并且在实际应用中能够高效地完成排序任务。
📌第4个参数—>compar比较函数的剖析
在 qsort
函数中,要实现升序或降序排序,这需要根据比较函数的逻辑来确定元素的顺序。比较函数的返回值将决定元素的排列方式。
- 如果比较函数返回负值,
qsort
将认为第一个元素应该在第二个元素之前,从而实现升序排序。 - 如果比较函数返回正值,
qsort
将认为第一个元素应该在第二个元素之后,从而实现降序排序。 - 如果比较函数返回零,
qsort
将认为两个元素相等,它们的顺序将是未定义的。
注意:
- qsort 函数的比较函数参数是两个 void 类型的指针,是为了提高灵活性和通用性。这样的设计允许您在不同的排序场景中使用同一个 qsort 函数,无论排序的数据类型是什么。
- 当编写一个通用的排序函数时,我们无法预先知道要排序的数据类型是什么。因此,将比较函数的参数声明为 void 类型的指针,使得 qsort 函数可以接受任何类型的数据
由此,我们可以得到qsort函数的使用模板如下:
#include <stdio.h> #include <stdlib.h> // 定义一个的数据类型(示例:整数类型) typedef int MyType; // 比较函数 int compareMyType(const void *a, const void *b) { return (*(MyType *)a - *(MyType *)b);//实现升序 //return (*(MyType *)b - *(MyType *)a);//实现降序 } int main() { int numElements = ...; // 数组中元素的数量 MyType arr[numElements]; // 声明并初始化一个数组 // 使用 qsort 对数组进行排序 qsort(arr, numElements, sizeof(MyType), compareMyType); // 打印排序后的数组 for (int i = 0; i < numElements; i++) { printf("%d ", arr[i]); // 打印数组元素 } return 0; }
三、qsort函数实例
注意:以下统一以升序为例
📌排序int类型数组
代码展示:
#include<stdio.h> #include<stdlib.h> //实现一个比较整型的函数 int compare_int(const void* a, const void* b) { return *(int*)a - *(int*)b;//强制转换为int类型并解引用 } //使用qsort对数组进行排序,升序 int main() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); printf("排序前:"); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } //排序 qsort(arr,sz,sizeof(int),compare_int); //打印 printf("\n排序后:"); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
运行结果:
📌排序char类型数组
代码展示:
#include <stdio.h> #include <stdlib.h> int compare_char(const void* a, const void* b) { return *(char*)a - *(char*)b; //强制转换为char类型并解引用 } int main() { char arr[] = { 'f', 'e','d','b','a','c' }; int sz = sizeof(arr) / sizeof(arr[0]); printf("排序前:"); for (int i = 0; i < sz; i++) { printf("%c ", arr[i]); } //排序 qsort(arr, sz, sizeof(arr[0]), compare_char); //打印 printf("\n排序后:"); for (int i = 0; i < sz; i++) { printf("%c ", arr[i]); } printf("\n"); return 0; }
运行结果:
📌排序浮点型数组
代码展示:
#include <stdio.h> #include <stdlib.h> int compare_float(const void* a, const void* b) { float num1 = *(float*)a; float num2 = *(float*)b; if (num1 < num2) return -1; if (num1 > num2) return 1; return 0; } int main() { float arr[] = { 5.2 , 2.5 , 3.14 , 1.5 }; int sz = sizeof(arr) / sizeof(arr[0]); printf("排序前:"); for (int i = 0; i < sz; i++) { printf("%f ", arr[i]); } //排序 qsort(arr, sz, sizeof(arr[0]), compare_float); //打印 printf("\n排序后:"); for (int i = 0; i < sz; i++) { printf("%f ", arr[i]); } printf("\n"); return 0; }
注意:
- 由于浮点数的精度和范围有限,返回差值可能导致精度丢失和不稳定的结果,特别是在极端情况下。因此,在处理浮点数时,使用差值可能会引发一些问题。
- 为了确保排序的稳定性和正确性,最好的做法是显式地使用 if 语句来比较元素的值,并返回 -1、0 或 1,以确保在各种情况下都能获得正确的比较结果。
运行结果:
📌排序结构体类型数组
代码展示:
1. 【按姓名来排序】
//按姓名来排序 #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct Student { char name[20]; int age; }stu; int compare_name(const void* a, const void* b) { return strcmp( ((stu*)a)->name, ((stu*)b)->name );//比较字符大小使用strcmp函数 //strcmp函数返回值与compare_name函数一致 } int main() { stu s[3] = { {"张三",20},{"李四",18},{"王五",25} }; int sz = sizeof(s) / sizeof(s[0]); printf("排序前:"); for (int i = 0; i < sz; i++) { printf("%s %d", s[i].name, s[i].age); if (i < sz - 1) printf(" | "); } //排序 qsort(s, sz, sizeof(s[0]), compare_name); //打印 printf("\n排序后:"); for (int i = 0; i < sz; i++) { printf("%s %d", s[i].name, s[i].age); if (i < sz - 1) printf(" | "); } printf("\n"); return 0; }
运行结果:
2. 【按年龄来排序】
代码展示:
//按年龄来排序 #include <stdio.h> #include <stdlib.h> typedef struct Student { char name[20]; int age; }stu; int compare_age(const void* a, const void* b) { return (((stu*)a)->age - ((stu*)b)->age); } int main() { stu s[3] = { {"张三",20},{"李四",18},{"王五",25} }; int sz = sizeof(s) / sizeof(s[0]); printf("排序前:"); for (int i = 0; i < sz; i++) { printf("%s %d", s[i].name, s[i].age); if (i < sz - 1) printf(" | "); } //排序 qsort(s, sz, sizeof(s[0]), compare_age); //打印 printf("\n排序后:"); for (int i = 0; i < sz; i++) { printf("%s %d", s[i].name, s[i].age); if (i < sz - 1) printf(" | "); } printf("\n"); return 0; }
运行结果: