qsort是<stdlib.h>库中快速排序算法的函数,其排序范围不单单为整形,也包括字符串,浮点型等数据。使用qsort需要传入数据地址,元素的个数及其宽度,和我们自定义的比较函数的地址。
冒泡排序通过两层循环,对数据进行两两比较并交换,但其只能排序整型。我们需要冒泡排序的循环比较逻辑,加上自定义的比较函数和交换函数,来改造出功能与qsort相似的my_sort函数。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>//使用冒泡排序的逻辑实现qsort函数;
int my_int_cmp(const void* e1, const void* e2)//比较函数
{
return *((int*)e1)- *((int*)e2);//需要将空类型的指针强制转换类型为我们需要的类型
}//对于整形来说,直接返回相减值,若返回值>0,则说明需要交换
void swap(char* j, char* J, int width)//交换函数(回调函数)
{
int i;
for (i = 0; i < width; i++)//两两交换第一个字节,直到第width个字节
{
char tmp = *j;
*j = *J;
*J = tmp;
j++;
J++;
}
}
void my_sort(void* arr, int sz, int width, int(*cmp)(const void* e1, const void* e2))//(注意)使用空类型接收数据地址,使用函数指针接收函数地址
{
int i, j;
for (i = 0; i < sz - 1; i++)//趟数
{
for (j = 0; j < sz - 1 - i; j++)//每一趟比较并交换元素的次数
{
if (cmp((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0.00)//进行比较,满足条件进行交换
{
swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);//传入需交换的两个元素地址和宽度
}
}
}
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_sort(arr, sz, sizeof(arr[0]), my_int_cmp);//输入和qsort相同类型的参数:数据起始地址,元素个数,宽度,回调函数的地址
int i;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
进入my_sort函数,借助两层冒泡循环,我们可以对数据两两比较。因为各类型数据的差异性,需要定义一个比较函数:以整型为例,假设需要排序的数据为一个整型数组。需要明确的是,传入比较函数my_int_cmp的地址用空类型指针接收,返回类型为整型。将传入的地址强制为整形并解引用就得到了我们需要比较的数据,直接返回两整型数据的差值,若差值>0,则进入条件语句进行交换。
void swap(char* j, char* J, int width)//交换函数(回调函数)
{
int i;
for (i = 0; i < width; i++)//两两交换第一个字节,直到第width个字节
{
char tmp = *j;
*j = *J;
*J = tmp;
j++;
J++;
}
}
明确需要交换的数据后,便进入swap函数进行交换,传入两数据的地址为(char*)类型,解引用访问的权限为1个字节,这就需要我们传入宽度,并借助循环,将数据的字节两两进行交换。因为无论什么类型的数据都以字节为单位储存,使这种交换的方式具有通用性,也就是说只要传入地址和宽度,便可以通过该函数来交换,实现了qsort函数的交换功能。
我们再以浮点型为例:
float my_float_cmp(const void* e1, const void* e2)//比较函数
{
return *((float*)e1) - *((float*)e2);//需要将空类型的指针强制转换类型为浮点型
}//对于浮点型来说,直接返回相减值,若返回值>0,则说明需要交换
//返回类型需要改为float避免数据丢失
我们只需要改变比较函数,就能实现多种类型数据的排序。
这种排序默认为升序,若把指针e1和e2交换或改变判断条件,则可以改为降序。
通过这次改造可以说明函数指针的重要性,将my_soort函数中比较和交换分开来分别交给swap和my_cmp,这使得回调函数起到一个外包的作用,将函数的某一功能外包给另一函数去做,避免了函数的冗杂,提高了可读性。