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都是第二行格子的地址。