qsort函数的理解和使用

目录

qsort函数

cmp函数的自定义

主函数

排序int类型代码

排序结构体

排序结构体的总代码(供读者测试)


qsort函数

qsort函数是<stdlib.h>库函数中的,基于快速排序算法的函数,可以对任意类型的数据进行排序

void qsort  (
			void* base,//待排序数组的起始位置
			szie_t num,//数组的元素个数
			size_t width,//一个元素的字节大小
			int(*cmp)(const void* el,const void* e2)//函数指针
			//cmp:比较函数,要求qsort函数的使用者自定义一个比较函数
			//如果排序的是结构体数据,就不方便直接使用<>比较了
			//使用者根据实际情况,提供一个函数
	//该函数的返回值需要:e1 < e2,返回负数,否则是整数或者是0(升序)
	//该函数的返回值需要:e1 > e2,返回负数,否则是整数或者是0(降序)
			//e1,e2是待比较的两个元素的地址
			
			);

该函数有四个参数:

void* base : 这是待排序数组的起始位置,我们肯定必须要先找到那个数组才能进行排序啊

size_t num : 需要我们传入该数组的元素个数

size_t width : 需要我们传入一个元素的字节大小,具体为啥需要这个数据,我们后面自然就懂了

int(*cmp)(const void* el,const void* e2) : 这个参数可能有一点难以理解,我们可以知道,这其实是一个函数指针,指向一个参数是const void* el,const void* e2,返回值是 int 的函数。这个函数需要我们使用者自己去创建,注意,参数和返回类型必须严格按照它给的那样写。至于这个函数里面写什么,请看下文。


cmp函数的自定义

cmp,顾名思义,是compare(比较),就是一个比较函数。那么为什么qsort函数不自己写,而是需要我们使用者自己写呢?因为在设计qsort函数的时候,我们并不能知道使用者比较的是什么类型的数据,我们没有办法写一个函数来比较所有可能的类型的数据,比如,如果我们排序的是结构体数据,我们必须告诉qsort函数应该使用什么样的规则来比较。

所以这个函数的内容就是,比较我们需要比较的数据类型,如果e1大于e2,那就返回一个大于零的数,反之,就返回一个小于零的数(显然,这是针对于升序排序的,如果你想要进行降序的排序,就要将结果的正负号反过来)。

int cmp_int(const void* e1,const void* e2)
//void类型的指针是不确定类型的指针,是不能直接解引用的
//它更像是一个垃圾桶,用来接受任意类型的指针,但是不能(p + 1)
//因为他不知道它步长是多少
{
	if (*(int*)e1 > *(int*)e2)
	{
		return 1;
	}
	else if (*(int*)e1 == *(int*)e2)
	{
		return 0;
	}
	else
	return -1;
}

但是这样写着太复杂了,我们不妨换个方式:

int cmp_int(const void* e1,const void* e2)
{
	
	return (*(int*)e1 - *(int*)e2) ;
}

即两个数相减,如果第一个数大于第二个数,那他们的差肯定就是负数了呗。

ps.我们习惯把这个函数命名为:cmp_数据类型,这样大大提高了程序的可读性。

写完这个函数,我们的准备工作就做好了


主函数

我们飞速地写出测试函数的主函数:

我们随便创建了一个乱序的数组,然后传入相应的参数到qsort函数中,并将最后的结果打印出来。

int main(void)
{
      int arr[] = {2,1,4,5,7,5,8,9};
      int sz = sizeof(arr) / sizeof(arr[0]);
      qsort(arr,sz,sizeof(arr[0]),cmp_int);
      for(int i = 0; i < sz; i++)
      {
        printf("%d ",arr[i]);
      }
      return 0;
}

排序int类型代码

int cmp_int(const void* e1, const void* e2)
{

	return (*(int*)e1 - *(int*)e2);
}

int main(void)
{
    int arr[] = { 2,1,4,5,7,5,8,9 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

这其实就已经实现我们的排序了,不过我们为了深入掌握这个函数,不妨多排序几组不同类型的数据


排序结构体

首先,我们要创建一个结构体

struct Stu 
{
	char name[20];
	int age;
	double score;
};

然后初始化结构体并写出qsort函数,这里我们为了更透彻理解这个函数,我们排序了两种,一种是结构体中的字符串排序(至于字符串怎么比较大小,请参考你查英语字典的顺序),和结构体中的整型的大小

int main(void)
{
    struct Stu arr[3] = {{"zhangsan",20,55.5},
						 {"lisi",30,88.0},
						 {"wangwu",50,90.0}};
	
    int sz = sizeof(arr)/sizeof(arr[0]);
	
    qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_age);
	qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
    return 0;
}

接着,我们就需要自定义我们的cmp函数了,注意定义的时候参数一定要按照规定的来,最好不要自己自由发挥哈(为了方便大家观看,我特地写成了这种代码风格,希望大家别介意)

int cmp_stu_by_age(const void* e1,const void* e2)
{
	return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}

int cmp_stu_by_name(const void* e1,const void* e2)
{
	return strcmp(
			((struct Stu*)e1)->name , ((struct Stu*)e2)->name
				 );
}

因为void*指针,电脑不知道他的步长,怎么可能知道每次解引用要访问后面多大的空间呢,所以在这里我们必须要进行强制类型转换,转换为结构体指针变量,然后用操作符访问age和name,之后比较大小。

当然,我上文提到过,用这种逻辑我们最后得到的是一个升序的数组,如果我们想要得到一个降序的数组怎么办呢?

我们拿age的排序举个例子吧:

//升序排序
int cmp_stu_by_age(const void* e1,const void* e2)
{
	return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
//降序排列:
int cmp_stu_by_age2(const void* e1,const void* e2)
{
	return (((struct Stu*)e2)->age - ((struct Stu*)e1)->age);
}

不难发现,只需要将返回值取个相反数(或者把减数和被减数交换位置,最终的效果都是一样的)。这样我们便能将一个数组进行降序的排序了,这便是qsort的强大之处,它允许你排序任何数据类型,也允许你自己设置排序的顺序。

最后运行即可


排序结构体的总代码(供读者测试)

//创建一个结构体类型
struct Stu 
{
	char name[20];
	int age;
	double score;
};
//升序排序年龄
int cmp_stu_by_age1(const void* e1,const void* e2)
{
	return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}
//降序排列年龄:
int cmp_stu_by_age2(const void* e1,const void* e2)
{
	return (((struct Stu*)e2)->age - ((struct Stu*)e1)->age);
}
//升序排序名字
int cmp_stu_by_name(const void* e1,const void* e2)
{
	return strcmp(
			((struct Stu*)e1)->name , ((struct Stu*)e2)->name
				 );
}
int main(void)
{
	struct Stu arr[3] = {{"zhangsan",20,55.5},
						 {"lisi",30,88.0},
						 {"wangwu",50,90.0}};
	int sz = sizeof(arr)/sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_age1);
    qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_age2);
	qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);
      
    return 0;
}

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值