C语言qsort函数使用详解

qsort函数参数

void qsort(voidBase,size_t_numberofelement,size_t_Sizeofelement,int(_cdecl_ptfuncCompare)(constvoid,constvoid));
第一个参数voidBase,这实际上是一个不定类型的指针,通常称为数组名,但是实际上并不局限于数组名,可以延伸出更多用途。
第二个参数size_t_numberofelement,数组中元素的个数。
第三个参数size_t_Sizeofelement,元素的大小。
第四个参数int(_cdecl
_ptfuncCompare)(constvoid,constvoid)),是一个自定义的排序规则。

自定义排序规则

以一维数组排序为例

int cmp(const void*a,const void*b){int c=*(int*)a,d=*(int*)b;return c-d;
}

cmp是自己定义的函数名,传入的参数是void*a,

是不定类型的指针,通过(int*)a,将其转换成一个数组指针,然后通过再在前面加“*”,使得c=该数组指针所指的对象。当返回值小于0时,交换指针a与指针b所指的对象。可以通过返回c-d,还是d-c来决定是升序还是降序排序。

一维数组排序

#include <stdio.h>
#include <stdlib.h>int cmp(const void*a,const void*b){int c=*(int*)a,d=*(int*)b;printf("a=%p  c=%d\n",a,c);printf("b=%p  d=%d\n",b,d);printf("*******************\n");return c-d;
}
int main(){int iarray[3]={2,5,3};printf("iarray=%p  iarray[0]=%d\n",iarray,iarray[0]);printf("*******************\n");qsort(iarray,3,sizeof(int),cmp);//对数组iarray按cmp规则进行排序,待排序元素个数为3,每个元素大小为sizeof(int)for(i=0;i<3;i++){printf("iarray[i]=%d   ",iarray[i]);}printf("*******************\n");system("pause");return 0;
}

结果如下

iarray=00CFF914  iarray[0]=2
a=00CFF918  c=5
b=00CFF914  d=2
*******************
a=00CFF91C  c=3
b=00CFF918  d=5
*******************
a=00CFF918  c=3
b=00CFF914  d=2
*******************
iarray[i]=2   iarray[i]=3   iarray[i]=5   请按任意键继续. . .

可以看到数组第一次传进cmp函数中指针b的地址就是数组iarray的首地址,而指针a的地址刚好就是下一个元素的地址。整个cmp被调用三次,采用冒泡排序的方法。

对部分数组进行排序

qsort的第一个参数void*Base实质上是一个指针,指明排序的起始位置,可以通过对其进行修改,结合第二个参数size_t_numberofelement(待排序元素数量),完成对部分数组进行排序。
例如在上面的例子中,将"iarray",改成“iarray+1”,将size_t_numberofelement参数从3改成2,就完成了只对数组后两个元素进行排序。
修改后代码

int main(){int iarray[3]={2,5,3};printf("iarray=%p  iarray[0]=%d\n",iarray,iarray[0]);printf("iarray+1=%p  iarray[0]=%d\n",iarray+1,iarray[0]);printf("*******************\n");qsort(iarray+1,2,sizeof(int),cmp);//对数组iarray按cmp规则进行排序,待排序元素个数为3,每个元素大小为sizeof(int)for(i=0;i<3;i++){printf("iarray[i]=%d   ",iarray[i]);}printf("*******************\n");system("pause");return 0;
}

结果如下:

iarray=0133FC58  iarray[0]=2
iarray+1=0133FC5C  iarray[0]=2
a=0133FC60  c=3
b=0133FC5C  d=5
*******************
iarray[i]=2   iarray[i]=3   iarray[i]=5   请按任意键继续. . .

可以看到指“iarray+1”所指的地址比“iarray”大4,正好是一个int大小。传进cmp函数中的指针b正好是iarray+1。两个元素排序,cmp调用一次。

同理,将“iarray+1”改成“&iarray[1]”的结果是一样的。

二维数组按某关键字排序

二维数组排序根据二维数组的定义方式不同而有所不同。

二维数组是通过int iarray[3][2];这种方式定义

这种方式定义的二维数组实质上还是一维数组,物理上在内存里表现为连续的空间。使用qsort函数是与一维数组大同小异,没有本质区别。

区别1:参数size_t_Sizeofelement,元素的大小

例如在二维数组int iarray[3][2]={{3,4},{2,3},{6,1}}中,一共有三个元素{3,4}、{2,3}、{6,1},每个元素是两个int大小,所以参数size_t_Sizeofelement应该为“sizeof(int)*2”。

区别2:cmp函数不同

int cmp(const void*a,const void*b){int *c=(int*)a,*d=(int*)b;return c[0]-d[0];
}

此时传进来的参数依然是一个不定类型的指针,不同的是,c和d定义成:a和b类型转换后的数组指针。这样做的好处是,当想根据二维数组中的第一列元素进行排序时,返回c[0]-d[0]或者d[0]-c[0];当想根据第二列元素进行排序时,返回c[1]-d[1]或者d[1]-c[1]。

实例:

#include <stdio.h>
#include <stdlib.h>int cmp(const void*a,const void*b){int *c=(int*)a,*d=(int*)b;printf("a=%p  c=%d\n",a,c);printf("b=%p  d=%d\n",b,d);printf("*******************\n");return c[0]-d[0];
}
int main(){int iarray2[3][2]={{3,4},{2,3},{6,1}};printf("iarray2=%p\n",iarray2);qsort(iarray2,3,sizeof(int)*2,cmp);for(i=0;i<3;i++){printf("iarray2[%d]=[%d %d] ",i,iarray2[i][0],iarray2[i][1]);}system("pause");return 0;
}

结果如下

iarray2=006FFEE0
a=006FFEE8  c[0]=2
b=006FFEE0  d[0]=3
*******************
a=006FFEF0  c[0]=6
b=006FFEE0  d[0]=3
*******************
a=006FFEE8  c[0]=2
b=006FFEE0  d[0]=3
*******************
iarray2[0]=[2 3] iarray2[1]=[3 4] iarray2[2]=[6 1] 请按任意键继续. . .

可以看到传进去的指针b依然是数组首指针。根据第一列3,2,6排序
当将cmp函数中的return c[0]-d[0];改成return c[1]-d[1];
结果为

iarray2[0]=[6 1] iarray2[1]=[2 3] iarray2[2]=[3 4] 请按任意键继续. . .

结果按第二列的4,3,1排序。

部分排序

与一维数组一样,在将“iarray2”改成“iarray2+1”就能够从二维数组的第二个开始排序。

二维数组通过动态分配malloc得到

根本区别

动态分配的二维数组在物理空间上不是连续的。

int main(){int **marray2=(int**)malloc(sizeof(int*)*3);int i;for(i=0;i<3;i++){marray2[i]=(int*)malloc(sizeof(int)*2);marray2[i][0]=10-i;marray2[i][1]=9-i;}for(i=0;i<3;i++){printf("marray2[%d]=[%d %d]  &marray2[%d]=%p &marray2[%d][0]=%p\n",i,marray2[i][0],marray2[i][1],i,&marray2[i],i,&marray2[i][0]);}
}

结果如下

marray2[0]=[10 9]  &marray2[0]=00158A90 &marray2[0][0]=00158AD8
marray2[1]=[9 8]  &marray2[1]=00158A94 &marray2[1][0]=00158B20
marray2[2]=[8 7]  &marray2[2]=00158A98 &marray2[2][0]=00158B68
请按任意键继续. . .

可以看到实际存储元素的地址并不相连,实际上malloc得到的地址空间如下图所示:在这里插入图片描述

cmp函数的区别

所以传进来的参数a,b是上图中第一行方格的地址,如果再将他通过(int*)转换成一维数组,当成一维数组来处理,明显就会出错,因为真正的元素并不在这里。而是应该通过(int**)转成二维数组来处理,这样,就能够找到真正存储元素的物理地址。

int cmpup(const void*a2,const void*b2){int *c2=*(int**)a2;int *d2=*(int**)b2;return c2[0]-d2[0];
}

(int**)b2,就是将传进来marray2的首地址(上图第一行第一个格子的地址)转换成二维数组地址格式,前面再加“*”就是取出该地址的内容(上图第2行第1个格子的地址),赋值给指针d2。这样d2就是我们想要的一维数组的首地址了。

qsort函数的区别

通过上述分析,在qsort函数中的size_t_Sizeofelement(元素大小)参数,就应该改成sizeof(int*)。因为传进cmp函数的指针是图中第一行格子的地址,每个格子里面装的是一个(int*)型的指针,其大小自然为sizeof(int*)。当然,也可以通过改变参数void*Base和size_t_numberofelement,来完成部分排序。

实例:

#include <stdio.h>
#include <stdlib.h>
int cmpup(const void*a2,const void*b2){int *c2=*(int**)a2;int *d2=*(int**)b2;printf("a2=%p  c2=%p  c2[0]=%d\n",a2,c2,c2[0]);printf("b2=%p  d2=%p  d2[0]=%d\n",b2,d2,d2[0]);printf("*******************\n");return c2[0]-d2[0];
}
int main(){int **marray2=(int**)malloc(sizeof(int*)*3);int i;for(i=0;i<3;i++){marray2[i]=(int*)malloc(sizeof(int)*2);marray2[i][0]=10-i;marray2[i][1]=9-i;}for(i=0;i<3;i++){printf("marray2[%d]=[%d %d]  &marray2[%d]=%p &marray2[%d][0]=%p\n",i,marray2[i][0],marray2[i][1],i,&marray2[i],i,&marray2[i][0]);}qsort(marray2,3,sizeof(int*),cmpup);for(i=0;i<3;i++){printf("marray2[%d]=[%d %d]  &marray2[%d]=%p\n",i,marray2[i][0],marray2[i][1],i,&marray2[i]);}system("pause");return 0;

结果如下

marray2[1]=[9 8]  &marray2[1]=00DB8AC4  &marray2[1][0]=00DB8B50
marray2[2]=[8 7]  &marray2[2]=00DB8AC8  &marray2[2][0]=00DB8B98
a2=00DB8AC4  c2=00DB8B50   c2[0]=9
b2=00DB8AC0  d2=00DB8B08  d2[0]=10
*******************
a2=00DB8AC8  c2=00DB8B98   c2[0]=8
b2=00DB8AC0  d2=00DB8B08  d2[0]=10
*******************
a2=00DB8AC4  c2=00DB8B50   c2[0]=9
b2=00DB8AC0  d2=00DB8B98  d2[0]=8
*******************
marray2[0]=[8 7]  &marray2[0]=00DB8AC0
marray2[1]=[9 8]  &marray2[1]=00DB8AC4
marray2[2]=[10 9]  &marray2[2]=00DB8AC8
请按任意键继续. . .

可以看到,每一次传进cmp的指针都是图中第一行格子的地址,转换后的c2和d2都是第二行格子的地址。

如有错误,恳请赐教


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部