首页 > 其他分享 >C语言——使用回调函数模拟实现qsort

C语言——使用回调函数模拟实现qsort

时间:2024-09-06 18:52:10浏览次数:13  
标签:sz arr int void qsort C语言 char width 模拟

同学们还记得之前我们已经学过一种排序方法叫“冒泡排序“嘛。代码直接附上咯


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-i-1; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
 
int main()
{
	int arr[] = { 6,4,1,2,9,3,7,8,10,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
 
	bubble_sort(arr,sz);
 
	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 现在我们已经找到了一些相应的问题,那我们怎么改造呢?

我们先参考学习一下库函数qsort或许你就会有自己的思路了,qsort的头文件<stdlib.h>

已经了解qsort需要包含的元素,那函数指针指向的比较函数我们要怎么去写呢,这边Mr.狠人举个例子哈

当然这样写是为了让你更好理解,如果能理解的话那我们可以简化代码

//自定义比较函数
int compar(const void* e1,const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

int main()
{
	int arr[10] = { 3,1,9,8,5,4,0,2,7,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), compar);
	
	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}

 当然我们也可以测试一下结构体数据能不能用qsort来排序

//创建一个结构体
struct students
{
	char name[20];
	int age;
};

//比较函数
int compar_students_by_age(const void* e1, const void* e2)
{
	return (*(struct students*)e1).age - (*(struct students*)e2).age;
}

int main()
{
	//为了方便理解我们就不演示回调函数,大家使用回调函数肯定更好
	struct students classA[3] = { {"jason",18},{"jack",28},{"leon",33} };
	int sz = sizeof(classA) / sizeof(classA[0]);
	qsort(classA, sz, sizeof(classA[0]), compar_students_by_age);
	
	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%d %s\n", classA[i].age,classA[i].name);
	}
	
	return 0;
}

当然我们也可以不按照age来比,我们用name来比,字符串的比较方式用strcmp函数比较

//创建一个结构体
struct students
{
	char name[20];
	int age;
};

//比较函数
//字符串的比较不能直接用大于号小于号
int compar_students_by_name(const void* e1, const void* e2)
{
	return strcmp((*(struct students*)e1).name,(*(struct students*)e2).name);
}

int main()
{
	struct students classA[3] = { {"jason",18},{"jack",28},{"leon",33} };
	int sz = sizeof(classA) / sizeof(classA[0]);
	qsort(classA, sz, sizeof(classA[0]), compar_students_by_name);
	
	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%d %s\n", classA[i].age,classA[i].name);
	}
	
	return 0;
}

 现在我们已经学会了qsort的使用方法,那么有没有思路去改造我们的冒泡排序了?还是把这张图拿下来哈

我们参考过qsort,我们是不是可以跟他一样用void*指针去接收所有类型的变量的地址

接下来是不是就要解决比较的问题,我们不知道使用者会传来的是什么类型的数组,可能是结构体对吧,那就不难单纯的用大于号小于号去比较

 那现在我们的冒泡排序的参数是不是就变成这样了

void bubble_sort(void* base, int sz, int width, int(*compar)(const void* e1, const void* e2))

比较函数的调用就是这样

int i = 0;//趟数
for (i = 0; i < sz - 1; i++)
{
	int j = 0;
	for (j = 0; j < sz - i - 1; j++)
	{
		//我们在这边已经有宽度了,那是不是全用char类型就行,因为char类型是一个字节,乘宽度刚好是我们需要的大小
		if(compar((char*)base+j*width, (char*)base + (j+1) * width)>0)//调用比较函数
		{
			//交换
		}
	}
}

现在就剩下交换了

那我们写个函数进行元素交换吧

    Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);

//交换函数
//调用时就已经强制转换成char*,所以这边直接写char*就行
void Swap(char* buf1, char* buf2, int width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

 要改的三个点我们都解决了,整理一下升级后的代码吧

//自定义比较函数(int)
int compar_int(const void* e1,const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//冒泡排序函数
void bubble_sort(void* base, size_t sz, size_t width, int(*compar)(const void* e1, const void* e2))
{
	int i = 0;//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - i - 1; j++)
		{
			
			if(compar((char*)base+j*width, (char*)base + (j+1) * width)>0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

//test函数
void test()
{
	int arr[] = { 6,4,1,2,9,3,7,8,10,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz, sizeof(arr[0]), compar_int);

	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

int main()
{
	test();
	return 0;
}

//创建一个结构体
struct students
{
	char name[20];
	int age;
};

//比较函数(结构体name)
int compar_students_by_name(const void* e1, const void* e2)
{
	return strcmp((*(struct students*)e1).name,(*(struct students*)e2).name);
}

//自定义比较函数(int)
int compar_int(const void* e1,const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

//交换函数
void Swap(char* buf1, char* buf2, size_t width)
{
	for (int i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//冒泡排序函数
void bubble_sort(void* base, size_t sz, size_t width, int(*compar)(const void* e1, const void* e2))
{
	int i = 0;//趟数
	for (i = 0; i < sz - 1; i++)
	{
		int j = 0;
		for (j = 0; j < sz - i - 1; j++)
		{
			
			if(compar((char*)base+j*width, (char*)base + (j+1) * width)>0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}

//test函数
void test()
{
	struct students classA[3] = { {"jason",18},{"jack",28},{"leon",33} };
	int sz = sizeof(classA) / sizeof(classA[0]);

	bubble_sort(classA, sz, sizeof(classA[0]), compar_students_by_name);

	//打印
	for (int i = 0; i < sz; i++)
	{
		printf("%s %d ", classA[i].name,classA[i].age);
	}
}

int main()
{
	test();
	return 0;
}

这样我们的代码也就写完了,通过这一次的改写冒泡排序,我们是不是能感受到void*指针的遍历,这种编程叫做泛型编程。

大家可以在自行梳理一下,有帮助请给一个三连吧! 

标签:sz,arr,int,void,qsort,C语言,char,width,模拟
From: https://blog.csdn.net/CPP_ZhouXuyang/article/details/141895294

相关文章

  • C语言-第七章:字符和字符串函数、动态内存分配
    传送门:C语言-第六章-加餐:其他自定义类型目录第一节:字符和字符串函数    1-1.strlen函数和sizeof关键字    1-2.memcpy内存拷贝函数    1-3.memmove内存拷贝函数    1-4.memset内存设置函数    1-5.strtok字符串切割函数......
  • C语言-第六章-加餐:其他自定义类型
    传送门:C语言-第六章:结构体目录第一节:位段    1-1.位段是什么    1-2.位段的大小第二节:联合体    2-1.联合体是什么    2-2联合体的大小第三节:枚举类型    3-1.枚举是什么第四节:结构体中的柔性数组    4-1.柔性数组......
  • C语言面向对象
    我们在编写程序时,通常采用以下步骤:将问题的解法分解成若干步骤使用函数分别实现这些步骤依次调用这些函数这种编程风格的被称作面向过程。除了面向过程之外,还有一种被称作面向对象的编程风格被广泛使用。面向对象采用基于对象的概念建立模型,对现实世界进行模拟,从而完......
  • C语言学习——sprintf函数详细解释及其用法
    文章目录函数功能:把格式化的数据写入某个字符串参数说明及应用举例解释:连接字符串打印地址信息利用sprintf的返回值使用sprintf的常见问题函数功能:把格式化的数据写入某个字符串头文件:stdio.h函数原型:intsprintf(char*buffer,constchar*format,[arg......
  • 『模拟赛』CSP-S模拟1
    Rank1BADA.喜剧的迷人之处在于签。正好早上还在改一个要分解质因数的题,所以一眼就出思路了。首先将\(a\)的平方因子全部除去,剩下的就是\(b\)必须的因数,即若设将平方因子全部除去后的\(a\)为\(a'\),则\(b\)应表示为\(a'\timesx^2\),从\(L\)这个下界开始只用找......
  • 使用flask进行Mock Server模拟接口操作及问题解决
    1.flask介绍flask是一个轻量级的pythonweb微框架2.MockServer介绍MockServer是一个开源的模拟服务器,它可以定义和记录API交互,支持各种http方法(get、post、put、delete),可以自定义响应内容,例如返回静态文件可以使用flask来搭建一个mock模拟服务3.模拟接口先安装flaskpip......
  • 学习C语言结构体(结构体的前世今生)
    1、首先我将使用DevC++这个软件(其实随意一个C++软件都可以)来演示一下结构体的使用方法。这里已经写了一个最简单的HelloWorld!程序。2、对于C语言的数据来说最重要的就是两个功能,一个是定义数据,一个是引用数据。既然结构体也是数据类型,那么他就和其他的数据类型差不多。也分......
  • 20240906_142048 c语言 认识c语言
    C语言是一种广泛使用的编程语言,它以其高效、灵活和接近硬件的特性而闻名。对于零基础的学生来说,学习C语言是一个很好的起点,因为它不仅能帮助你理解计算机程序的基本结构和概念,还能为学习更高级的编程语言(如C++、Java、Python等)打下坚实的基础。下面我将简要介绍C语言的一些基本概念......
  • C语言之动态内存分配与释放
    C语言之动态内存分配与释放通用指针类型void通用类型指针具有以下特点:类型无关,赋值灵活:由于指针本质上是一个存储内存地址的变量,而内存地址是没有类型的,所以void指针可以存储任意类型数据的地址,指向任意类型对象。无论是整数、浮点数、字符或数组、结构体等类型都可以用void指......
  • 9.6 上午 becoder 模拟赛总结&题解
    T1语言水题不多说,很容易发现NP需要满足的只是最后一个单词为N,前面是A或N都可以随意放。所以用两个数组,\(v1_i\)记录以\(i\)结尾的前缀是否可以构成NP,\(v2_i\)记录以\(i\)为开头的后缀是否可以构成NP。最后for循环扫一遍是否有同时满足\(v1_{i-1}=true\)和......