本文介绍:
1.qsort函数的构成
2.qsort的使用
3.用qsort的实现原理模拟实现可排序所有类型数据的冒泡排序
自我介绍:一个脑子不好的大一学生,c语言接触还没到半年,若涉及到效率等问题,各位都可以在评论区提出见解,谢谢啦。
该账号介绍:此帐号会发布游戏(目前还只会简单小游戏),算法,基础知识等内容。
文章特点:会将重要步骤和易错点在代码中用注释标示(方便各位理解和定位)
qsort是一个强大的函数,它可以比较任何类型的数据,整型已是so easy,它还可以比较浮点数,字符,甚至是结构体,但是先别急,容我先讲讲它的构成再将其使用
由图可知,qsort函数的返回类型为int,第一个参数为void*,第二个和第三个参数为size_t,也就是unsigned int,第四个参数为函数指针,其参数为const void*,返回类型为int,在此,为了方便大家理解,我将其函数构成简化为如下部分:
void qsort(void* base,
? ? ? ? ? ? ? ? size_t num,
? ? ? ? ? ? ? ? size_t width,
? ? ? ? ? ? ? ? int(cmp)(const void*e1,const void*e2)
? ? ? ? ? ? ? ? )
那个星号格外耀眼是不是,因为所有要比较的东西都是类似于数组的东西啦,必须要用指针传参哦,让它知道要比较的地方是哪里
为什么为void呢?而不是整型?
因为qsort是一个强大的函数啦,它不能独宠一种数据,否则会引起公愤滴!
void是泛型,它可以接受任何数据类型,因此这里是void啦
这就不用过多解释啦
个数不能为负数啦
因为它可以接受任何数据,因此必须传给它一个大小才方便进行比较
该函数的返回类型
该函数的两个参数的
比较函数(现在不懂别着急,后面你就知道啦),返回类型为int,参数为const void*,这里为const void*的原因与之前一样,它方便接受各种类型的数据
函数调用约定,这里就需要你自行了解啦,它在这里作用不大,我就不进行叙述啦
(这里就主要介绍cmp比较函数的构成啦,其他部分在后续代码中就能理解啦)
比较函数,我将对它分为自定义类型数据比较和自带类型分别进行介绍
我们要设计一个比较函数,先要搞清它的返回类型和参数,而这里在前面的qsort函数的介绍部分就可知:
由此,我们可以构造出它的框架啦:
int cmp(const void*e1,const void*e2)
{
}
要对其进行比较大小,是不是要对其进行解引用呢?
那写成这样行不行?(本文默认为升序排列)
int cmp(const void*e1,const void*e2)
{
return (*e2-*e1);
//若为降序:return (*e1-*e2);
}
答案是:错误
原因:因为它是void*类型,void*类型不可被解引用,因此要对它进行强制类型转换
正确写法:
若为整数:
int cmp(const void*e1,const void*e2)
{
return (*(int*)e2-*(int*)e1);
//若为降序:return (*(int*)e1-*(int*)e2);
}
若为字符:
int cmp(const void*,const void*)
{
return strcmp(*(char*)e1,*(char*)e2);
}
下面我用整型来举例:
int main()
{
int arr[10]={10,9,8,7,6,5,4,3,2,1};
int sz=sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp);
//调用函数的方法
//arr:数组名
//sz:数组元素个数
//sizeof(arr[0]):元素大小
//cmp:比较函数
return 0;
}
以下为结构体的调用:
struct STU
{
char name[20]={0};
int age;
};
//注意有分号哦
int cmp(const void*,const void*)
{
return ((struct STU*)e1->age - (struct STU*)e2->age);
//与其他原理相同,要转化为对应的类型
}
int main()
{
struct STU s[]={{“zhangsan”,15},{“lisi”,30},{“wangwu”,25}};
//注意结构体变量的定义方法
int sz=sizeof(s)/sizeof(s[0]);
qsort(s,sz,sizeof(s[0]/**/),cmp);
//调用函数的方法
return 0;
}
以上框架还不可完全实现排序操作,下面我来用qsort函数的构成原理来写一个冒泡排序吧
int main()
{
int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
qsort_mao_pao_pai_xu(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),cmp);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
void qsort_mao_pao_pai_xu(void* base,int sz,int width, int (*cmp)(const void*, const void*))
{
for (int i = 0; i < sz-1; i++)
{
int flag = 0;
//标记部分
for (int j = 0; j < sz - 1 - i; j++)
{
/*if ((*cmp)(base[j], base[j+1]))*/
//错误示范:因为cmp要的是地址
/*if ((*cmp)(base+j, base+j+1))*/
//错误示范:因为base是void*,要强制类型转换
if ((*cmp)(/**/(char*)base + j * width/**/, (char*)base + (j + 1) * width)>0)
{
//调用比较函数
/*char*的原因:因为可能不一定时int类型的数字转化,char的幅度小,更灵活*/
flag = 1;
Swap((char*)base + j * width/**/, (char*)base + (j + 1) * width, width/*因为要知道调换几个字节*/);
}
}
if (flag == 0)
//如果内循环进行完没有,说明后续已经是升序,可以直接跳出外循环,避免无效循环,提高效率
break;
}
}
void Swap(char* e1, char* e2,int width/*因为要知道调换几个字节*/)
{
//因为传参时已被强制类型转换为char*,所以参数直接设置为char*就可以
for (int i = 0; i < width; i++)
{
//一个字节一个字节的调换,更灵活
int t = *e1;
*e1 = *e2;
*e2 = t;
e1++;
e2++;/**/
}
}
好啦,还有其他三种排序算法在我前面的文章已写过啦,大家可以去看看哦