您好,欢迎访问代理记账网站
移动应用 微信公众号 联系我们

咨询热线 -

电话 15988168888

联系客服
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

回调函数与qsort函数的爱恨情仇

目录

一,简单冒泡排序

1.1冒泡排序含义

1.2冒泡排序两步走

1.3完整代码

二,qsort函数实现 

2.1qsort函数分析

2.2qsort函数实现结构体类型的排序

2.3自定义函数实现qsort的功能

 三,回调函数


一,简单冒泡排序

1.1冒泡排序含义

所谓冒泡排序,也即就是那个数大,这个数就跟泡泡一样升起来。所以今天我们来了解一下它具体实现的过程。为了方便后续qsort函数的学习,这里冒泡排序程序稍微复杂一点。

首先,我们这里只用了整型数据进行冒泡排序,所以我们采用整型数组。函数只需要传递数组名以及数组元素个数。然后,主要函数实现部分分析如下:

1.2冒泡排序两步走

首先,我们分两步进行排序,第一步,从该数组首元素开始,与首元素后面的元素两两进行比较,如果前面的元素大于后面的元素,则交换两元素的位置,反之不换,然后第二个和第三个,依次比较,直到该组元素全部比较完,改数组最后一个元素即为这组数最大的数。

这便是第一步,之后第二步。因为第一步之后,最后一个元素即为最大的数,故不需要再比较,此时,便可以将排查范围缩小到数组元素减一,再进行第一步,此时找到的是次大值,这样一直找,直到数组首元素是最小值。这便是第二步。

但是需要额外注意的是第一步中的判断条件,只能是前面数大于后面时才换。其次需要注意的就是第二步中的每次一趟排序完之后,下一次查表元素减少一位。这里 j 的值应该是在 i 的基础上再减去 i 。

1.3完整代码

#include<stdio.h>
void bubble_sort(int arr[],int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
 		{
			if (arr[j] > arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j+1] = temp;
			}
		}
	}
}
void print1(int arr[], int sz)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
}
int main()
{
	//int arr[10] = {0,1,2,3,4,5,6,7,8,9};
	int arr[10] = {9,8,7,6,5,4,3,2,1,0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr,sz);
	print1(arr,sz);
	return 0;
}

二,qsort函数实现 

首先我们需要知道是,qsort函数就是冒泡排序函数。所以qsort函数内部是可以帮助我们实现很多功能的,但是我们知道,既然是排序,那就不可能仅仅是整型数据,当然有可能是浮点型,字符型,结构体这些了,那既然不是整型排序,上面的方法一定是不行的。

这就是为什么我们需要学习qsort函数,因为该函数可以实现任何类型数据的排序。

2.1qsort函数分析

接下来,我们来了解一下该函数的的基本格式:

//sort基本格式
void qsort(void* base,
          size_t num, 
          size_t size,
	      int (*compar)(const void*, const void*));

如上所示,qsort函数参数部分一共分为四部分,接下来我们一一解读:

首先 void * base ,即为一个返回类型为 void 的指针。这将返回值类型设为 void 的原因是现实中会有很多种数据类型,设为 void 方便我们需要使用其他类型时,强制转换。既然这里是是指针,那么即可以有很多种指针类型,这就实现了我们本文开头那种只能实现整型指针的概念。 

除此之外,中间两个参数分别为元素个数,一个元素的大小。都是无符号整型,因为元素个数和元素大小不能为负数,所以用 size_t 这种无符号整型。 

最后一个参数是一个函数指针,该函数实现的功能为比较两个元素的大小,如果前面一个大于后面,返回一个大于0的数,如果相等返回一个等于0的数,如果小于,则返回一个小于0的数。然后qsort函数内部根据返回的值类型,再决定是否交换两个元素的位置。

2.2qsort函数实现结构体类型的排序

好的,了解了其基本框架之后,我们写一个结构体类型元素的排序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[10];
	int age;
};
int swap(const void* e1, const void*e2)
{
	return strcmp(((struct stu *)e1) ->name , ((struct stu*)e2) ->name);
}
int main()
{
	//结构体类型用在qsort函数
	struct stu s [3]= { {"zhangsan",29} ,{"lisi",26}, {"wangwu",30} };
	int num = sizeof(s) / sizeof(s[0]);
	qsort(s,num,sizeof(s[0]),swap);
	return 0;
}

 这里可以看到,我们只需要对qsort进行传递参数之后,再实现一个具体的交换函数。当然这里我们也可以实现年龄的比较。当然,我们通过另一种方式看排序的结果:

 可以看到这是我们通过名字排序之后的结果,这里是通过调试窗口展示的结果。

2.3自定义函数实现qsort的功能

因为qsort函数可以对任意类型的数据进行排序,所以接下来我们根据上面qsort函数实现自定义该函数。代码如下:

#include<stdio.h>
#include<string.h>
struct stu
{
	char name[10];
	int age;

};
int cmp_sort_age(const void* x, const void* y)
{
	return ((struct stu*)x)->age - ((struct stu*)y)->age;
}
swap_cmp(char *buf1,char *buf2,int size)
{
    int i = 0;
	for (i = 0; i < size; i++)
	{
		char temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}
void q_sort(void * base, size_t num, size_t size, int (*cmp)(const void* ,const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num-1; i++)
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base + j * size, (char*)base + (j+1) * size) > 0)
				swap_cmp((char*)base + j * size, (char*)base + (j + 1) * size,size);
		}
	}
}
int main()
{
    struct stu s[3] = { {"zhangsan",34 },{"lisi",21},{"wangwu0",30} };
	int sz = sizeof(s) / sizeof(s[0]);
    //按名字排序
	//qsort(s,sz,sizeof(s[0]),cmp_sort_name);
	//strtmp函数的返回值
	//按年龄排序
	q_sort(s, sz, sizeof(s[0]), cmp_sort_age);
	return 0;
}

那么这个程序首先的逻辑是结构体初始化之后,自定义函数q_sort传递参数,在q_sort函数内部实现最基础的cmp函数两两比较,此时cmp_sort_age函数返回两数比较的值是否大于0,若是, 则调用swap_cmp函数交换两变量值,最终完成整个结构体数组按年龄项的冒泡排序。

这里不太容易理解的是,cmp函数的参数以及swap_cmp函数的参数。解释如下:

因为原函数参数是void * ,所以首先得强制类型转换为char类型指针,因为char类型是最小的数据类型,任何数据在内存中都可表示以字节为单位的数据,所以以 1 字节为最小单位,指针加 j 乘以 元素大小 size ,另一个则为 j+1 个元素,表示后面一位元素。即表示两个个元素比较大小。这也是swap_cmp函数为什么以char类型为单位进行比较。

如图所示为按年龄排序的结果: 

 三,回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

所以说,如果没有回掉函数,程序将会变得很冗杂,有时候甚至导致函数不能运行。

所以小伙伴们,你们觉得这两者之间是否相伴相生呢?欢迎留言讨论哦!

今天的内容就到这儿了,如有错误,还请指正喔!!万分感谢!!

 

 


分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进