顺德移动端网站建设,企业文化墙设计图效果图,医院网站优化,云南省建设厅标准员网站#x1f341;博客主页#xff1a;江池俊的博客 #x1f4ab;收录专栏#xff1a;C语言进阶之路 #x1f4a1;代码仓库#xff1a;江池俊的代码仓库 #x1f3aa;我的社区#xff1a;GeekHub #x1f389;欢迎大家点赞#x1f44d;评论#x1f4dd;收藏⭐ 文章目录 前… 博客主页江池俊的博客 收录专栏C语言进阶之路 代码仓库江池俊的代码仓库 我的社区GeekHub 欢迎大家点赞评论收藏⭐ 文章目录 前言一、什么是回调函数使用回调函数的优势 二、qsort 函数及其用法qsort函数作用qsort函数4个参数的介绍为什么qsort函数的参数是这四个第4个参数---compar比较函数的剖析 三、qsort函数实例排序int类型数组排序char类型数组排序浮点型数组排序结构体类型数组1. 【按姓名来排序】 2. 【按年龄来排序】 四、模拟实现qsort函数冒泡排序bubble_sort函数模拟实现的qsort函数Swap函数剖析 利用bubble_sort函数排序整型数组利用bubble_sort函数排序结构体数组1. 【按姓名来排序】2. 【按年龄来排序】 总结 前言 回调函数和 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类型数组
代码展示
#includestdio.h
#includestdlib.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.hint 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.hint 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;
}运行结果
四、模拟实现qsort函数 这里我是基于冒泡函数的思路来实现qsort函数的实际上qsort函数的排序思路是快速排序 冒泡排序的设计在本篇文末 冒泡排序
#includestdio.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;}}}
}
int main()
{int arr[] { 3,1,7,5,8,9,0,2,4,6 };int sz sizeof(arr) / sizeof(arr[0]);printf(冒泡排序前\n);for (int i 0; i sz; i){printf(%d , arr[i]);}//冒泡排序bubble_sort(arr, sz);printf(\n冒泡排序后\n);for (int i 0; i sz; i){printf(%d , arr[i]);}printf(\n);return 0;
}运行结果 这里我们发现bubble_sort函数中用来接收待排序数组首元素地址的指针arr已经被写死了是int*类型这就表它只能对整型数组进行排序。其次函数内部对数组元素的比较和交换只适用于int类型的数据。 现在将利用冒泡排序来实现qsort函数让它能排序任意类型的数据该怎么做呢
首先我们知道qsort函数的创作者他并不知道我们将来需要排序什么类型的数组但是呢他却通过qsort函数实现了各种类型数组的排序这是怎么做到的呢这就得益于这个函数的4个参数了。因此只要我们给qsort函数提供 待排序数组首元素的地址数组中元素的个数数组中每个元素所占内存空间的字节大小以及一个 比较函数 就能实现对这个数组的排序。所以我们也可以通过这些参数来用冒泡排序的思想实现对任意类型数组的排序。
bubble_sort函数模拟实现的qsort函数 值得注意的是这里说的利用冒泡排序来实现qsort函数仅仅是实现了qsort函数可以对任意类型的数组进行排序这一特点并不是说实现了qsort函数的底层原理qsort的底层其实是通过快速排序来实现的。 //利用冒泡排序实现qsort
void Swap(char* e1, char* e2, size_t width)
{int i 0;for (i 0; i width; i){char tmp *e1;*e1 *e2;*e2 tmp;e1;e2;}
}
//注意这里的compar函数需要根据待排序的类型来书写
void bubble_sort(void* arr, int sz, size_t width, int(*compar)(const void* e1, const void* e2))
//第一个参数 - 用来接收待排序数组的首元素地址因为待排序的数组元素类型不确定所以形参数组用void*来接收
//第二个参数 - 用来接收数组元素个数
//第三个参数 - 用来接收数组中每个元素的大小单位是字节
//第四个参数 - 用来接收一个比较函数根据待排序数组元素的类型来传递对应类型的比较函数
{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 (compar((char*)arr j * width, (char*)arr (j 1) * width) 0)//因为我们并不知道数组元素的类型所以需要将元素转化为最小的char*类型//即把arr强转为char*类型arr就可以正常使用且char*与width配合能访问到任意类型任意位置处的数组元素//char类型指针1只会跳过一个字节 j*width表示跳过j个元素{//交换//由于这里的数组名已经被强转为char类型的指针//所以要交换数组中的元素就只能一个字节一个字节进行交换Swap((char*)arr j * width, (char*)arr (j 1) * width, width);//前两个参数是待交换元素的地址第三个参数是待交换元素的所占字节的大小flag 0;//如果数组元素进行交换了说明数组还没有排好序} }if (flag 1)//如果没有再交换数组元素就说明数组已经排好序{break;}}
}Swap函数剖析
Swap 函数用于交换两个元素的内容它接受三个参数这三个参数的作用如下
void Swap(void* e1, void* e2, size_t width);void* e1: 指向第一个待交换元素的指针。由于数组的元素类型是未知的所以使用 void* 类型来表示元素的指针。在函数内部你需要将其转换为正确的类型以便进行元素交换。 void* e2: 指向第二个待交换元素的指针。同样你需要在函数内部将其转换为正确的类型以便进行交换操作。 size_t width: 表示每个元素所占的字节数。由于元素类型未知但在 bubble_sort 函数中有提供所以通过这个参数确保在进行元素交换时能够正确地按字节进行操作。 在 Swap 函数内部通过使用 width 参数以字节为单位逐个交换两个元素的内容。这种设计使得 Swap 函数在不知道元素实际类型的情况下仍能够正确地交换元素内容。虽然在实际代码中可能会使用更高级的语言特性来进行元素交换例如 C 中的模板函数或 C 中的宏但是在这个示例中通过使用 void* 指针和 字节级的操作实现了一个通用的元素交换函数。 利用bubble_sort函数排序整型数组
代码展示
#includestdio.h
//利用bubble_sort函数排序整型数组
void Swap(char* e1, char* e2, size_t width)
{int i 0;for (i 0; i width; i){char tmp *e1;*e1 *e2;*e2 tmp;e1;e2;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(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 (compar((char*)arr j * width, (char*)arr (j 1) * width) 0)//实现升序{Swap((char*)arr j * width, (char*)arr (j 1) * width, width);flag 0;//如果数组元素进行交换了说明数组还没有排好序}}if (flag 1)//如果没有再交换数组元素就说明数组已经排好序{break;}}
}
//比较函数
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//主函数
int main()
{int arr[] { 3,1,7,5,8,9,0,2,4,6 };int sz sizeof(arr) / sizeof(arr[0]);printf(排序前\n);for (int i 0; i sz; i){printf(%d , arr[i]);}//排序bubble_sort(arr, sz, sizeof(int), cmp_int);printf(\n排序后\n);for (int i 0; i sz; i){printf(%d , arr[i]);}printf(\n);return 0;
}运行结果
利用bubble_sort函数排序结构体数组
1. 【按姓名来排序】
代码展示
//按姓名来排序
#include stdio.h
#include stdlib.h
#include string.h
//利用bubble_sort函数排序结构体数组
void Swap(char* e1, char* e2, size_t width)
{int i 0;for (i 0; i width; i){char tmp *e1;*e1 *e2;*e2 tmp;e1;e2;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(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 (compar((char*)arr j * width, (char*)arr (j 1) * width) 0)//实现升序{Swap((char*)arr j * width, (char*)arr (j 1) * width, width);flag 0;//如果数组元素进行交换了说明数组还没有排好序}}if (flag 1)//如果没有再交换数组元素就说明数组已经排好序{break;}}
}
//声明一个结构体并重命名为stu
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函数返回值与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( | );}//排序bubble_sort(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
//利用bubble_sort函数排序结构体数组
void Swap(char* e1, char* e2, size_t width)
{int i 0;for (i 0; i width; i){char tmp *e1;*e1 *e2;*e2 tmp;e1;e2;}
}void bubble_sort(void* arr, int sz, size_t width, int(*compar)(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 (compar((char*)arr j * width, (char*)arr (j 1) * width) 0)//实现升序{Swap((char*)arr j * width, (char*)arr (j 1) * width, width);flag 0;//如果数组元素进行交换了说明数组还没有排好序}}if (flag 1)//如果没有再交换数组元素就说明数组已经排好序{break;}}
}
//声明一个结构体并重命名为stu
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( | );}//排序bubble_sort(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;
}运行结果 总结
回调函数和 qsort 是 C 语言编程中重要的概念能够提供强大的灵活性和功能。通过理解回调函数的概念我们可以将特定行为作为参数传递给其他函数实现代码的模块化和解耦合。qsort 则为数组排序提供了便利允许我们自定义比较逻辑以满足不同的需求。
通过以上的介绍和模拟实现希望初学者们能够更好地理解回调函数和 qsort 的核心概念为日后的编程实践打下坚实的基础。无论是构建灵活的程序结构还是优化代码性能这些概念都将成为你编程工具箱中不可或缺的工具。 今天的分享就到这里 如果觉得博主的文章还不错的话 请三连支持一下博主哦