C语言qsort函数介绍

前言

学到了函数指针,那这篇博客我们可以根据函数指针,了解一个函数qsort的应用与模拟实现

欢迎关注个人主页:小张同学zkf

若有疑问   评论区见


目录

 

1.回调函数

2.qsort函数使用

3.qsort模拟实现


1.回调函数

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

简单来说,就是把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这就是回调函数的功能。
 

2.qsort函数使用

qsort是一个库函数,函数功能就是快速排序,可以将数组里的东西按照一定的顺序升序降序排列

qsort的形式:

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

我们来一个一个分析,第一个参数是指针指向的是待排序数组的第一个元素,由于不知道你会传递什么指针类型,就用void*(是无具体类型的指针,它的作用就是接收任何类型的地址)暂时来作为指针类型。第二个参数是base指向的待排序数组的元素个数,第三个参数是每个元素的大小(字节长度),最后一个传入的是一个函数compar的指针,指向的是两个元素的比较函数。

举一个例子:

#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}

这个是排序整形数据,我们可以看到,arr首元素地址,元素个数,每个元素的大小,以及比较方式都传入了qsort函数,这个比较函数int_cmp,在qsort函数内部调用,返回的是整型类型的两个元素的整型差,根据结果推断,如果第一个元素比第二个元素大,返回的是大于零的数,按的是升序排列,如果返回时两个元素交换位置,返回的若是大于零的数,则是降序排列

那如果不是整型数组能排列吗,我要是结构体类型那

struct Stu //学⽣
{
    char name[20];//名字
    int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
    struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
    int sz = sizeof(s) / sizeof(s[0]);
    qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
    test2();
    test3();
    return 0;
}

可以看到结构体类型也可以按照某个成员类型来排列数组。

所以若使用qsort函数快速排序时这个比较函数必须自己定义好比较方式,然后把它的地址作为参数传入qsort内部进行调用。就能实现qsort快速排序的功能。

3.qsort模拟实现

那这个函数内部到底是怎么实现的那,qsort函数是快速排序的功能,那我们可以先想想我之前发的指针博客里的冒泡排序。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void maopao(int arr[], int x)
{
	for (int i = 0; i < x-1; i++)//趟数
	{
		int s = 1;//假设有序
		for (int j = 0; j <x-i-1 ; j++)//一趟比较几次
		{
			if (arr[j]>arr[j+1])
			{

				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j+1] = tmp;
				s = 0;//不完全有序
			}
		}
		if (s == 1)//以完全有序
		{
			break;
		}
	}
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	maopao(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		
		printf("%d ", arr[i]);
	}
	return 0;
}

两个for循环,外部那个for循环代表一共要比较几趟,如果10个数比较9趟,11个数比较10趟可见最后一个数不需要再走,内部那个for循环代表一趟要比较几次,第一个数比较,除了自身以外比较元素个数减1次,第二个数开始比较时由于已经和第一个数比较过了,就不需要再和它比较,所以比较次数再减1,所以随着趟数的增加,比较次数依次减小,直到完全有序。

那我们想想,qsort内部是不是也是类似于冒泡排序那,我们来模拟一下


void qsort_s(void* a, size_t x, size_t whits, int (*campare)(const void* as1, const void* as2))
{
	//趟数
	for (int i = 0; i < x-1; i++)
	{
		for (int j = 0; j < x - i - 1; j++)//每一趟
		{
			if (campare((char*)a + j*whits, (char*)a+(j+1)*whits) > 0)//满足交换的条件
			{
				swap((char*)a + j * whits, (char*)a + (j + 1) * whits,whits);//内部一个单元一个单元的交换
			}
		}
	}


}
void print(int arr[], int x)
{
	for (int i = 0; i < x; i++)
	{
		printf("%d ",arr[i]);
	}
}
int main()
{
	int arr[] = { 23,5,67,89,36,2,300};
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort_s(arr, sz, sizeof(arr[0]), campare);
	print(arr, sz);

	return 0;
}

我们假设qsort_s是模仿qsort的函数,qsort不仅仅可以排列整型,一切类型都可以排序,所以元素和下一个元素之间的字节宽度我们要考虑到,就是第三个参数whits,两次for循环内部是一个判断条件,就是比较函数返回值是否大于零,大于零就交换,也就是传入这个函数的地址是为了在此处调用比较函数进行判断是否达成交换的条件,在判断的时候不管元素地址是什么类型都要将它强制转换成char*类型,是因为我们交换这俩元素,这俩元素不管什么类型都是俩内存块,内存块交换要内部一个字节一个字节交换,从而达到两个元素的整体交换。

从这里我们也看出qsort函数内部模仿原理跟冒泡排序有点类似。

我们在定义比较函数时,也要注意,传过来的元素地址不管什么类型,都要将它强制转换成整型指针,这是因为因为比较函数返回值就是整型。以下是比较函数的定义

int campare(const void* as1, const void* as2)
{
	return *(int*)as1 - *(int*)as2;

}

别忘了定义交换函数

void swap(char* cv1, char* cv2, size_t whits)
{
	for (int i = 0; i < whits; i++)//一一对应交换
	{
		char tmp = *cv1;
		*cv1 = *cv2;
		*cv2 = tmp;
		cv1++;
		cv2++;
	}
}

以下是qsort函数模拟实现的所有代码 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int campare(const void* as1, const void* as2)
{
	return *(int*)as1 - *(int*)as2;

}
void swap(char* cv1, char* cv2, size_t whits)
{
	for (int i = 0; i < whits; i++)//一一对应交换
	{
		char tmp = *cv1;
		*cv1 = *cv2;
		*cv2 = tmp;
		cv1++;
		cv2++;
	}
}
void qsort_s(void* a, size_t x, size_t whits, int (*campare)(const void* as1, const void* as2))
{
	//趟数
	for (int i = 0; i < x-1; i++)
	{
		for (int j = 0; j < x - i - 1; j++)//每一趟
		{
			if (campare((char*)a + j*whits, (char*)a+(j+1)*whits) > 0)//满足交换的条件
			{
				swap((char*)a + j * whits, (char*)a + (j + 1) * whits,whits);//内部一个单元一个单元的交换
			}
		}
	}


}
void print(int arr[], int x)
{
	for (int i = 0; i < x; i++)
	{
		printf("%d ",arr[i]);
	}
}
int main()
{
	int arr[] = { 23,5,67,89,36,2,300};
	int sz = sizeof(arr) / sizeof(arr[0]);

	qsort_s(arr, sz, sizeof(arr[0]), campare);
	print(arr, sz);

	return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/429878.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

春日特惠,爱基百客限时放送,开启您的学术新篇章!

春回大地&#xff0c;万物复苏&#xff0c; 正是探索未知、启发新思的最佳时节。 在这个充满生机的季节里&#xff0c; 我们推出了春季大促活动&#xff0c; 旨在助力每一位科研工作者在新的一年里实现更多突破。 让我们一起迎接科研人的春天&#xff0c; 开启智慧的花朵…

基本设计模式

单例模式 ES5 function Duck1(name:string){this.namenamethis.instancenull }Duck1.prototype.getNamefunction(){console.log(this.name) }Duck1.getInstancefunction(name:string){if(!this.instance){this.instance new Duck1(name)} } const aDuck1.getInstance(a) const…

Jetpack Compose: Hello Android

Jetpack Compose 是一个现代化的工具包&#xff0c;用于使用声明式方法构建原生 Android UI。在本博文中&#xff0c;我们将深入了解一个基本的 “Hello Android” 示例&#xff0c;以帮助您开始使用 Jetpack Compose。我们将探讨所提供代码片段中使用的函数和注解。 入门 在…

C++ //练习 10.31 修改前一题的程序,使其只打印不重复的元素。你的程序应使用unique_copy(参见10.4.1节,第359页)。

C Primer&#xff08;第5版&#xff09; 练习 10.31 练习 10.31 修改前一题的程序&#xff0c;使其只打印不重复的元素。你的程序应使用unique_copy&#xff08;参见10.4.1节&#xff0c;第359页&#xff09;。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09…

基于ERNIR3.0的文本多分类

还在用BERT做文本分类&#xff1f;分享一套基于预训练模型ERNIR3.0的文本多分类全流程实例【文本分类】_ernir 文本分类-CSDN博客 /usr/bin/python3 -m pip install --upgrade pip python3-c"import platform;print(platform.architecture()[0]);print(platform.machine…

深度学习500问——Chapter02:机器学习基础(3)

文章目录 2.10 主成分分析&#xff08;PCA&#xff09; 2.10.1 主成分分析&#xff08;PCA&#xff09;思想总结 2.10.2 图解PCA核心思想 2.10.3 PCA算法推理 2.10.4 PCA算法流程总结 2.10.5 PCA算法主要优缺点 2.10.6 降维的必要性及目的 2.10.7 KPCA与PCA的区别 2.11 模型评估…

什么是跨站脚本攻击(XSS)

厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OCP\CKA\K8S\ CISP\CISSP\PMP\ ​ 跨站脚本攻击&#xff08;Cross-site Scripting&#xff0c;通常称为XSS&#xff09;&#xf…

MCU设计--M3内核详解(2)

内核架构 FETCH取指单元DEC指令译码EXEC执行LSU内存取数ETM_INTF调试接口STATUS状态上报 内核-寄存器 不同指令集支持不同的寄存器分配。R13用于主堆栈指针(MSP)&#xff0c;进程堆栈指针(PSP)R13 连接寄存器存储子程序指针&#xff0c;提高速度R15 程序计数器PC 剩下的一些 …

Gemma模型一些细节讲解

Gemma模型报告中提到的几个点进行代码细节解读一下&#xff1a; &#xff08;1&#xff09;Embedding层共享参数 &#xff08;2&#xff09;输入输出层均进行RMSNorm Embedding层共享参数 共享embedding的权重给最后的llm_head层。是词嵌入层的共享&#xff0c;与旋转位置编码…

羊大师揭秘羊奶探秘,不止于营养的美味饮品

羊大师揭秘羊奶探秘&#xff0c;不止于营养的美味饮品 羊奶作为一种古老而珍贵的乳制品&#xff0c;不仅具有丰富的营养价值&#xff0c;还拥有独特的口感和风味&#xff0c;使其成为一种不止于营养的美味饮品。以下是对羊奶的深入探秘&#xff1a; 独特的风味&#xff1a;羊…

【JavaEE进阶】 Linux搭建Java部署环境

文章目录 &#x1f343;前言&#x1f334;Linux权限&#x1f6a9;用户操作&#x1f6a9;三种角色&#x1f6a9;文件类型和访问权限&#x1f388;文件类型&#x1f388;基本权限 &#x1f6a9;修改文件权限 &#x1f38d;搭建Java部署环境&#x1f6a9;apt&#x1f388;apt常用命…

【C++基础】STL容器面试题分享||上篇

&#x1f308;欢迎来到C基础专栏 &#x1f64b;&#x1f3fe;‍♀️作者介绍&#xff1a;前PLA队员 目前是一名普通本科大三的软件工程专业学生 &#x1f30f;IP坐标&#xff1a;湖北武汉 &#x1f349; 目前技术栈&#xff1a;C/C STL 1.请说说 STL 的基本组成部分2.详细的说&…

08 OpenCV 腐蚀和膨胀

文章目录 作用算子代码 作用 膨胀与腐蚀是数学形态学在图像处理中最基础的操作。其卷积操作非常简单&#xff0c;对于图像的每个像素&#xff0c;取其一定的邻域&#xff0c;计算最大值/最小值作为新图像对应像素位置的像素值。其中,取最大值就是膨胀&#xff0c;取最小值就是腐…

Pytorch学习 day01(Jupyter安装、常用函数、三种编辑器的对比)

Jupyter 安装过程中遇到的问题&#xff1a; Anaconda的base环境会自动安装Jupyter&#xff0c;但是如果我们要在其他环境中安装Jupyter&#xff0c;就需要注意&#xff0c;该环境的python版本不能高于3.11&#xff0c;且用以下代码安装&#xff1a; conda install nb_conda_…

【深度学习】脑部MRI图像分割

案例4&#xff1a;脑部MRI图像分割 相关知识点&#xff1a;语义分割、医学图像处理&#xff08;skimage, medpy&#xff09;、可视化&#xff08;matplotlib&#xff09; 1 任务目标 1.1 任务简介 本次案例将使用深度学习技术来完成脑部MRI(磁共振)图像分割任务&#xff0c…

java spring 03 启动细节

启动类ClassPathXmlApplicationContext public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, Nullable ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}}其中…

扬帆启航!携手飞桨get开源贡献新技能!

亲爱的开发者朋友们&#xff0c;“飞桨启航计划第二期”正式启动啦&#xff01;这是一个专为开源爱好者设计的远程项目&#xff0c;旨在通过集训营的形式&#xff0c;鼓励大家积极参与到开源项目中来&#xff0c;提升代码实践能力&#xff0c;并与飞桨开源社区共同成长&#xf…

Sublime Text4代码配色自定义方案

文章目录 前言Settings设置效果图 前言 关于Sublime Text对于我的使用体验&#xff0c;只能说内置的代码主题真的都太low了&#xff0c;一点都不好看。所以接下来我分享一下我自定义代码配色。当然&#xff0c;大家也可以通过我给的中文翻译注释来自定义自己喜欢的颜色。废话不…

【数字经济】数字化转型与制造企业绿色创新质量(2000-2022)

数据来源&#xff1a;企业年报等时间跨度&#xff1a;2000-2022年数据范围&#xff1a;A股上市制造企业数据指标&#xff1a; 股票代码 省份代码 总资产 年份 城市代码 企业年龄 股票简称 上市状态 资产负债率 行业名称 绿色专利申请 企业成长 …

面试经典150题——逆波兰表达式求值

Man cannot live like a beast, he should pursue knowledge and virtue. -- Dante 1. 题目描述 2. 题目分析与解析 2.1 思路一 这个波兰式我记得在之前上编译原理的时候学过&#xff0c;是对输入的代码进行解析用的。可能有一部分读者对于波兰表达式并不太熟悉&#xff0c;…