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得到的地址空间如下图所示:C语言qsort函数使用详解_第1张图片

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

如有错误,恳请赐教

你可能感兴趣的:(C语言,c语言,排序)