C语言库函数qsort——使用方法及其模拟实现

目录

一、使用方法:

(一)使用说明:

(二)函数声明:

(三)对参数使用的说明:

(四)简单使用实例:

 二、C语言模拟实现qsort:

(一)、步骤一

(二)、步骤二

(三)、步骤三

(四)、模拟实现源代码


一、使用方法:

(一)使用说明:

①:该函数是对数组中的数据进行排序。

②:与其他排序算法(如冒泡排序,快速排序....)不同的是:用这些排序算法只能对一种类型的数据进行排序,而用此库函数则可以对所有类型进行排序(如结构体,整形,浮点型等等),这也是他的优点所在。

③:该库函数的原理为:快速排序算法。

④:该库函数用到了“回调函数”的机制。

⑤:具体用法可在库函数官网:https://legacy.cplusplus.com/搜索了解。

(二)函数声明:

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*));

解释:

1:该函数无返回类型。

2:总共有四个参数:

           ①:第一个参数base,用于接收待排序的数组的首地址,因为可以排序所有类型的数组,所有参数类型为“void*”(类型void*,可以接收不同类型的地址,具体用法可以自行了解。)。

          ②:第二个参数num,即待排序数组(该指针所指向)的元素个数,参数类型是无符号整形。

          ③:第三个参数size,即待排序数组(该指针所指向)中每个元素的大小,参数类型为无符号整形。

          ④:第四个参数是一个函数指针(没学过但感兴趣的伙伴可自行学习,若不感兴趣就照着下述方法使用此库函数即可),我们通常叫做“比较函数”,所指向的函数返回类型为int,有两个“const void*”的参数,该指针指向的函数需要我们(用户)自行定义,常用定义方法如下:

        

int comp1(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

解释:

            ①:对于形参的两个指针,是用来接收数组中所比较的两个元素的地址,我们不用管,此库函数会根据我们qsort前三个形参自动传值,这里只需记住,用void*来接收,是为了方便接收不同类型的数据,又因为是我们用户自己定义,所以我们根据实际情况来强制转换类型,如上述则是排序整形,所以强制转换为“int*”,const的作用就是防止两个地址被改变。

           ②:对于返回类型,我们需记住,返回用指针p1的值减去p2的值,即为升序排序;

                                                                 返回用指针p2的值减去p1的值,即为降序排序。

如上述用例中,即为升序排序。

(三)对参数使用的说明:

①:为什么base类型用"void*"?

答:方便接收不同类型的数据的起始地址。

②:为什么需要数组的元素个数num?

 答:因为需要对数组进行排序。

③:为什么需要数组元素的大小szie

答:因为在排序的时候程序不知道所排序的数据是什么类型,不知道一步走多远,所以需要每个元素的大小,以便完成排序。

(四)简单使用实例:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>

//升序排序整形的比较函数
int comp1(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
//降序排序整形的比较函数
int comp2(const void* p1, const void* p2)
{
	return (*(int*)p2 - *(int*)p1);
}
//打印数组
void my_print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);
	}
	printf("\n");
}

//排序整形用例
void test1()
{
	int arr[10] = { 2,3,1,6,5,8,4,0,7,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int size = sizeof(arr[0]);
	printf("原arr:");
	my_print(arr, sz);
	//升序
	qsort(arr, sz, size, comp1);
	printf("升序:");
	my_print(arr, sz);
	//降序
	qsort(arr, sz, size, comp2);
	printf("降序:");
	my_print(arr, sz);
}

//排序结构体用例
typedef struct Stu
{
	int age;
	char name[10];
}S;
//排序结构体成员age的比较函数
int comp3(void* p1,void* p2)
{
	return (((S*)p1)->age - ((S*)p2)->age);
}
//排序结构体成员name的比较函数
int comp4(void* p1, void* p2)
{
	return strcmp(((S*)p1)->name, ((S*)p2)->name);
}

void test2()
{
	S s[3] = { {20,"zhangsan"}, {10,"lisi"},{40,"wangwu"} };
	//按升序年龄排序
	qsort(s, 3, sizeof(s[0]), comp3);
	int i = 0;
	printf("\n按升序年龄排序:\n");
	for (i = 0; i < 3; i++)
	{
		printf("%s %d\n", s[i].name, s[i].age);
	}
	//按姓名升序排序
	qsort(s, 3, sizeof(s[0]), comp4);
	printf("按姓名升序排序:\n");
	for (i = 0; i < 3; i++)
	{
		printf("%s %d\n", s[i].name, s[i].age);
	}
}

int main()
{
	test1();//排序整形
	test2();//排序结构体
	return 0;
}

       值得注意的是:在比较函数中,整形,浮点型等等类型的可以直接用大于符号进行比较,但对于一个字符串(如用例中比较name的比较函数comp4)则不行  ,这时用到了字符串比较函数strcmp(相关用法可以自行了解,或者https://legacy.cplusplus.com/搜索了解)。  

运行结果:

 二、C语言模拟实现qsort:

(一)、步骤一

1. 上述介绍到,qsort函数的原理为“快速排序”,但本次模拟实现,小编用的是冒泡排序

2. 既然是模拟实现,所以返回类型,参数类型和个数都应该一致。

3. 冒泡排序的基本思想无非是,一层for循环确定趟数,一层for循环确定每一趟需要交换的次数,这里就需要用到第二个参数num用于确定趟数,num个元就走num-1趟。

void my_bubb_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	//趟数
	for (i = 0; i < num; i++)
	{
		int j = 0;
		//每趟的交换次数
		for (j = 0; j < num - 1 - i; j++)
		{
			
		}
	}
}

(二)、步骤二

4. 到了这里就准备判断是否进行交换了,这里就用到了第四个参数函数指针,用来调用用户所传的实参地址所指向的函数(这就是“回调函数”的经典实例),具体怎么判断如下图:

void my_bubb_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	//趟数
	for (i = 0; i < num; i++)
	{
		int j = 0;
		//每趟的交换次数
		for (j = 0; j < num - 1 - i; j++)
		{
			//以升序为例,用户自定义的函数comp1和comp2返回值大于0,则交换
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
				
			}
		}
	}
}

具体看到if的判断条件中,调用函数指针cmp所指函数,并把要比较的两个元素的地址作为实参传进去,这里就用到了第三个形参size,为什么这样写,解释如下:

而局部变量j的值恰好符合寻址条件,所以size的倍数为j倍。

然后再看到,函数指针cmp所指向的函数的返回值判断是否大于0 ,若大于0,则对实参量地址所对应的两个数据进行交换。这就对应了用户自定义的比较函数中的返回方式:

当为*p1-*p2时(*p1为前一个数,*p2为后一个数):因为规则是返回大于0的数才交换,所以只有当*p1>*p2时,返回值才为正数,这样才会交换*p1和*p2的值,所以较大的数会被排到后面,所以此时是升序排序。

 当为*p2-*p1时(*p1为前一个数,*p2为后一个数):根据规则,只有当*p2>*p1时,返回值才为正数,这样才会交换*p1和*p2的值,所以较大的值会被排在前面,所以是降序排序。

(三)、步骤三

5. 当判断是否排序之后,排序条件成立,进入if 后就该进行交换操作,这里定义了一个函数Swap用于此操作。

函数定义如下:

void Swap(char* buf1, char* buf2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}
void my_bubb_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	//趟数
	for (i = 0; i < num; i++)
	{
		int j = 0;
		//每趟的交换次数
		for (j = 0; j < num - 1 - i; j++)
		{
			//以升序为例,用户自定义的函数comp1和comp2返回值大于0,则交换
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
				//交换
				Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

如上述代码,可以看到我们需要将满足条件的两个数的地址作为实参传给Swap,同时还要将元素大小size传进去,所以形参为两个char*的指针buf1和buf2,以及用于接收元素大小的size。

传两个数的地址是为了交换两个数,那传元素大小size是为什么呐?

原因如下图:

根据上述原理,buf1和buf2都是char*指针,每次只能交换一字节的内容,交换完后对指针buf1和buf2进行加1,这样便跳到下一字节的内容,所以想要交换完全,就需要交换size次,因为元素大小为size字节,一次交换一字节,所以交换size次即可达到目的,所以for循环判断条件为i<size。

上面即是模拟实现的所有内容,下面是使用实例:

(四)、模拟实现源代码

//冒泡排序模拟实现qsort

int comp1(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
	return (*(int*)p2 - *(int*)p1);

}

void Swap(char* buf1, char* buf2,int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}

}

void my_bubb_sort(void* base, int num, int size, int(*cmp)(const void*, const void*))
{
	int i = 0;
	//趟数
	for (i = 0; i < num; i++)
	{
		int j = 0;
		//每趟的交换次数
		for (j = 0; j < num - 1 - i; j++)
		{
			//以升序为例,用户自定义的函数comp1和comp2返回值大于0,则交换
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0)
			{
				//交换
				Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

int main()
{
	int i = 0;
	int arr[10] = { 6,5,8,7,3,4,2,1,9,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("原arr:");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	my_bubb_sort(arr, sz, sizeof(arr[0]), comp1);
	printf("\n升序排序:");
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

运行结果:

本次知识到此结束,希望对你有所帮助!!!

猜你喜欢

转载自blog.csdn.net/hffh123/article/details/132261992