C语言qsort函数详解(模拟实现)

本篇是C语言的笔记,如果想要学习C语言笔记可以看作者之前的笔记,从C语言最初的常见概念、数组、函数、分支和循环到现在的指针。都是好文哦!欢迎大家去了解一下。

在上一篇的最后提到了一个回调函数,回调函数简单来说就是将一个函数的地址赋值给函数指针,在另一个函数中达到某种特定条件时就回调这个函数指针被称之为回调函数。

本篇会围绕着讲解qsort函数中还有内部的回调函数的部分要给大家讲解,正片开始。

1、什么是qsort函数

qsort函数的功能是数组排序,给定任意类型的数组,再给定数组元素的信息便可以排序。不管是char数组int数组还是自定义结构体数组都是可以排序的。所以qsort函数就是数组排序的函数。

2、qsort的函数声明和头文件包含

qsort函数是包含在stdlib.h头文件中的,如果我们想调用qsort函数就需要包含对应头文件

#include <stdlib.h>

调用qsort函数时我们需要传递4个参数,如下qsort的函数声明:

void qsort(void* base,数组的首元素地址
           int size,  数组的元素个数
           int width, 数组中每个元素的大小
           int (*con)(const void*,const void*) 函数指针
           )

void* base参数是因为qsort可以排序各种类型的数组,所以要用void*的指针来接收各个类型的数组首元素地址。因为void*可以接收各个类型的数据,相当于一个万能的存储空间。但是不能解引用,因为void*只是个没有类型的地址,没有访问权限。

int size参数是数组中元素个数,在给数组排序时需要得知数组有多少个元素。方便找到这个范围内的元素循环并排序。

int width参数是数组中每个元素的大小,因为传进来的地址是void类型,并不知道这个数组的每个元素有多大空间的访问权限,所以需要有一个元素大小的信息方便交换这么大的元素。

int (*con)(const void*,const void*)参数是函数指针,这个函数指针用来接收我们传递的函数,而这个函数需要我们自己去定义实现并传参,后面会讲到该函数的作用。

3、qsort函数的调用

知道了qsort的功能和参数。那就开始尝试调用一下qsort函数。

#include <stdlib.h>
int con(const void* e1, const void* e2)//该函数是自己实现的,要排序什么类型数组,就将地址强转成什么类型
{
	return *(int*)e1 - *(int*)e2;//qsort内部判断该函数返回值如果大于0就交换,如果等于或小于则不交换
}//如果e1大于e2,e1-e2一定返回大于0的数字,e1大于e2就交换
void print_f(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);//求数组元素个数
	qsort(arr, sz, sizeof(arr[0]), con);//数组排序
	print_f(arr, sz);//数组打印
	return 0;
}

运行结果:

我们传递的函数con是qsort达到某种特定条件回调这个函数进行判断的,判断两个元素的大小。满足条件就交换。所以具体如何判断还是要自己实现。

既然知道了qsort如何调用那我们可以模拟实现qsort函数,看一看qsort函数的底层原理。

4、qsort函数的模拟实现

如果我们要模拟qsort函数就需要得知排序方法。我们知道的排序方法有很多种,但是我们要使用最常用的排序也就是冒泡排序bubble_sort来模拟实现qsort函数。

那什么是冒泡排序呢?

4.1 冒泡排序

 冒泡排序是一种数组排序的算法,这种排序就像汽水里的气泡一样不停的从下往上面冒泡,所以名为冒泡排序(bubble sort)。

冒泡排序的算法思想就是需要排序n-1趟,每一趟排出最大的数在位置最后。因为这种方法最多n-1趟就可以将数组排序完毕。因为每次筛选最大值排在最后,有n个数,n-1个数筛选完后最后一个数必定是在第一个,也就是最小值。经过第一趟排序需要n-1次判断两个相邻的数,如果前面大于后面的就调换。算上排最大值本身,与其他的值经过筛选判断也只需要n-1次判断排出最大值。每一趟排出最大数下一趟排序的n-1需要再减去前面已经排过的趟数。因为每一趟都排出最大值下一趟就不需要对最大数也进行判断,只需要判断已排序最大值前面的那些值就可以了。

既然知道了冒泡排序算法的思想,那接下来就实现冒泡排序算法:

int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i, j;
	for (i = 0; i < sz - 1; i++)//循环排序n-1趟
	{
		int flag = 1;//假设顺序是正确的
		for (j = 0; j < sz - 1 - i; j++)//循环n-1-i次判断并调换找出最大值
		{
			if (arr[j] > arr[j + 1])
			{
				int s = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = s;
				flag = 0;//设置为需要排序
			}
		}
		if (flag == 1)//假设一趟下来没有任何排序的值,说明已经不再需要排序,跳出循环排序
		{
			break;
		}
	}
    for (i = 0; i < sz; i++)
    {
	    printf("%d ", arr[i]);
    }
	return 0;
}

运行结果:

4.2 模拟实现

了解了冒泡排序后我们就可以以冒泡排序来实现qsort函数了,我们自定义模拟实现的qsort函数就以bubble_sort为函数名,接下来就是bubble_sort自定义函数的实现:

void reverse(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)//交换元素大小个字节的元素刚好是交换数组的两个元素
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int size, int width, int(*con)(const void*, const void*))
{
	int i, j;
	for (i = 0; i < size - 1; i++)
	{
		int flag = 1;
		for (j = 0; j < size - 1 - i; j++)
		{
			if (con((char*)base + j * width, (char*)base + (j + 1) * width))//达到某种特定条件调用该函数,这就是回调函数
			{
				reverse((char*)base + j * width, (char*)base + (j + 1) * width, width);//通过char*有一个字节的权限,再配合元素的大小+j刚好跳过这个元素大小的整数倍
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

达到某种特定条件回调我们传参的函数,就是回调函数,以上con就是回调函数。

但是该函数最精巧的不是回调函数,而是(char*)base+j*width(char*)base+(j+1)*width,因为该函数的特点就是可以排序每个类型的数组,既然是这样,必须要用void*的指针base来接收地址。再将地址强转成char*类型,每次+j*width刚好跳过这些元素的大小来到某个元素的位置并调换。这就是void*指针和每个元素的大小在该函数中的作用。

然后我们就调用该函数并打印:

int con(const void* e1, const void* e2)//该函数是自己实现的,要排序什么类型数组,就将地址强转成什么类型
{
	return *(int*)e1 - *(int*)e2;//qsort内部判断该函数返回值如果大于0就交换,如果等于或小于则不交换
}//如果e1大于e2,e1-e2一定返回大于0的数字,e1大于e2就交换
void print_f(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);//求数组元素个数
	bubble_sort(arr, sz, sizeof(arr[0]), con);//数组排序
	print_f(arr, sz);//数组打印
	return 0;
}

打印结果:

以上就是qsort函数功能和模拟实现的讲解,到这里也就结束了,如果有哪个地方不懂可以在评论区留言,再见

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值