qsort函数使用方法总结
目录
一、qsort函数原型
二、compar参数
三、各种类型的qsort排序
1. int 数组排序
2. 结构体排序
3. 字符串指针数组排序
4. 字符串二维数组排序
四、回调函数
1. 什么是回调函数
2. 为什么要用回调函数?
3. 怎么使用回调函数?
4.下面是一个四则运算的简单回调函数例子:
五、qsort函数的模拟实现
一、qsort函数原型
void qsort( void *base,size_t nmemb,size_t size,int (*compar)(const void *, const void *));
头文件:<stdlib.h> 函数功能:qsort()函数的功能是对数组进行排序,数组有nmemb个元素,每个元素大小为size。
参数base - base指向数组的起始地址,通常该位置传入的是一个数组名
参数nmemb - nmemb表示该数组的元素个数
参数size - size表示该数组中每个元素的大小(字节数)
参数(*compar)(const void *, const void *) - 此为指向比较函数的函数指针,决定了排序的顺序。
函数返回值:无
注意:如果两个元素的值是相同的,那么它们的前后顺序是不确定的。也就是说qsort()是一个不稳定的排序算法。
二、compar参数
compar参数是qsort函数排序的核心内容,它指向一个比较两个元素的函数,注意两个形参必须是const void *型,同时在调用compar 函数(compar实质为函数指针,这里称它所指向的函数也为compar)时,传入的实参也必须转换成const void *型。在compar函数内部会将const void *型转换成实际类型,见下文。
int compar(const void *p1, const void *p2);
如果compar返回值小于0(< 0),那么p1所指向元素会被排在p2所指向元素的前面,如果compar返回值等于0(= 0),那么p1所指向元素与p2所指向元素的顺序不确定,如果compar返回值大于0(> 0),那么p1所指向元素会被排在p2所指向元素的后面。因此,如果想让qsort()进行从小到大(升序)排序,那么一个通用的compar函数可以写成这样:
int compare (const void * a, const void * b){if ( *(MyType*)a < *(MyType*)b ) return -1;if ( *(MyType*)a == *(MyType*)b ) return 0;if ( *(MyType*)a > *(MyType*)b ) return 1;}
注意:你要将MyType换成实际数组元素的类型。 或者
//升序排序
int compare(const void* a, const void* b)
{return (*(int*)a - *(int*)b);
}
//降序排列
int compare(const void* a, const void* b)
{return (*(int*)b - *(int*)a);
}
三、各种类型的qsort排序
1. int 数组排序
#include <stdio.h>
#include <stdlib.h> int values[] = { 40, 10, 100, 90, 20, 25 };int compare(const void* a, const void* b)
{return (*(int*)a - *(int*)b);
}int main()
{int n;qsort(values, sizeof(values) / sizeof(values[0]), sizeof(int), compare);for (n = 0; n < sizeof(values) / sizeof(values[0]); n++)printf("%d ", values[n]);return 0;
}
2. 结构体排序
#include <stdio.h>
#include<stdlib.h>
// void qsort(void* base, size_t num, size_t size, int(*compare)(const void*, const void*))typedef struct
{char name[30]; // 学生姓名int Chinese; // 语文成绩int Math; // 数学成绩 int English; // 英语成绩
}st;
int cmp(const void* a, const void* b)
{st* pa = (st*)a;st* pb = (st*)b;int num1 = pa->Chinese + pa->English + pa->Math;int num2 = pb->Chinese + pb->English + pb->Math;//return (int)num1 - num2; // 从小到大,return (int)num2 - num1; // 从大到小
}
int main(void)
{st students[7] = {{"周",97,68,45},{"吴",100,32,88},{"郑",78,88,78},{"王",87,90,89},{"赵",87,77,66},{"钱",59,68,98},{"孙",62,73,89}};qsort(students, 7, sizeof(st), cmp); // 注意区别 students 与 stfor (int i = 0; i < 7; i++){printf("%s%4d%4d%4d\t", students[i].name, students[i].Chinese, students[i].Math, students[i].English);printf("总分:%d\n", students[i].Chinese + students[i].English + students[i].Math);}system("pause");return 0;
}
3. 字符串指针数组排序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int compare(const void* arg1, const void* arg2);int main(int argc, char** argv)
{int i;char* arr[5] = { "i", "love", "c", "programming", "language" };qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(char*), compare);for (i = 0; i < 5; i++) {printf("%s ", arr[i]);}printf("\n");}int compare(const void* arg1, const void* arg2) {char* a = *(char**)arg1;char* b = *(char**)arg2;int result = strcmp(a, b);if (result > 0) {return 1;}else if (result < 0) {return -1;}else {return 0;}
}
那么我们向qsort传入arr之后,qsort将arr理解为指向数组中第一个元素的指针,所以形参表中,arg1和arg2其实是指向“指向常量字符串的指针”的指针,是char**。而我们需要传给strcmp这个字符串比较函数的,是“指向字符串的指针”,是char*,所以我们将void*转换为char**,然后解引用,得到char*,赋予a和b。接下来使用strcmp对a和b进行比较。(数组名本身算一层指针,而里面的内容又是一层指针,数组存放的是指向字符串的地址)
4. 字符串二维数组排序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int compare(const void* arg1, const void* arg2);int main(int argc, char** argv)
{int i;char arr[5][16] = { "i", "love", "c", "programming", "language" };qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), compare);printf("%s\n", arr[0]);for (i = 0; i < 5; i++) {printf("%s ", arr[i]);}printf("\n");
}int compare(const void* arg1, const void* arg2)
{char* a = (char*)arg1;char* b = (char*)arg2;int result = strcmp(a, b);if (result > 0) {return 1;}else if (result < 0) {return -1;}else {return 0;}
}
这里对二维数组进行排序,其实是对二维数组的第二维中存放的字符串进行排序。所以qsort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]), compare);对qsort函数的调用中,第二个参数是待排元素的个数(5个),第三个参数是待排元素的大小(16)。
我们将arr传入qsort函数,qsort函数将arr理解为指向数组第一个元素的指针,arr的第一个元素是arr[0][0],所以参数arg1和arg2指的是指向"a[i][0]"的指针,我们知道,a[i][0]是字符,就是char,所以arg1和arg2指的是char *。我们将void*转换为char*,赋予a和b,调用strcmp函数对a和b进行比较。
四、回调函数
1. 什么是回调函数
我们先来看看百度百科是如何定义回调函数的:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
这段话比较长,也比较绕口。下面我通过一幅图来说明什么是回调:

假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。
结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。
我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。
如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2. 为什么要用回调函数?
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。

int Callback() ///< 回调函数
{// TODOreturn 0;
}
int main() ///< 主函数
{// TODOLibrary(Callback); ///< 库函数通过函数指针进行回调// TODOreturn 0;
}
回调似乎只是函数间的调用,和普通函数调用没啥区别。
但仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。
这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀。
3. 怎么使用回调函数?
#include <stdio.h>
int Callback_1(int a) ///< 回调函数1
{printf("Hello, this is Callback_1: a = %d \n", a);return 0;
}int Callback_2(int b) ///< 回调函数2
{printf("Hello, this is Callback_2: b = %d \n", b);return 0;
}int Callback_3(int c) ///< 回调函数3
{printf("Hello, this is Callback_3: c = %d \n", c);return 0;
}int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{Callback(x);
}int main()
{Handle(4, Callback_1);Handle(5, Callback_2);Handle(6, Callback_3);return 0;
}
看看结果:
如上述代码:可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1() / Callback_2() / Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。
4.下面是一个四则运算的简单回调函数例子:
#include <stdio.h>
#include <stdlib.h>/***************************************** 函数指针结构体***************************************/
typedef struct _OP {float (*p_add)(float, float);float (*p_sub)(float, float);float (*p_mul)(float, float);float (*p_div)(float, float);
} OP;/***************************************** 加减乘除函数***************************************/
float ADD(float a, float b)
{return a + b;
}float SUB(float a, float b)
{return a - b;
}float MUL(float a, float b)
{return a * b;
}float DIV(float a, float b)
{return a / b;
}/***************************************** 初始化函数指针***************************************/
void init_op(OP* op)
{op->p_add = ADD;op->p_sub = SUB;op->p_mul = &MUL;op->p_div = &DIV;
}/***************************************** 库函数***************************************/
float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
{return (*op_func)(a, b);
}int main(int argc, char* argv[])
{OP* op = (OP*)malloc(sizeof(OP));init_op(op);/* 直接使用函数指针调用函数 */printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2),(op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));/* 调用回调函数 */printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n",add_sub_mul_div(1.3, 2.2, ADD),add_sub_mul_div(1.3, 2.2, SUB),add_sub_mul_div(1.3, 2.2, MUL),add_sub_mul_div(1.3, 2.2, DIV));return 0;
}
五、qsort函数的模拟实现
使⽤回调函数,模拟实现qsort(采⽤冒泡的⽅式)。
注意:这里第⼀次使用 void* 的指针,讲解 void* 的作⽤。
#include <stdio.h>int cmp_int(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}void Swap(char* buf1, char* buf2, size_t width)
{int i = 0;for (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 (*cmp)(const void* p1, const void* p2))
{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])if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){/*int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;*///交换Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}void test()
{//设计和实现bubble_sort2(),这个函数能够排序任意类型的数据int arr[] = { 3,1,5,7,9,2,4,0,8,6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main()
{test();return 0;
}相关文章:
qsort函数使用方法总结
目录 一、qsort函数原型 二、compar参数 三、各种类型的qsort排序 1. int 数组排序 2. 结构体排序 3. 字符串指针数组排序 4. 字符串二维数组排序 四、回调函数 1. 什么是回调函数 2. 为什么要用回调函数? 3. 怎么使用回调函数? 4.下面是…...
机器学习介绍与分类
随着科学技术的不断发展,机器学习作为人工智能领域的重要分支,正逐渐引起广泛的关注和应用。本文将介绍机器学习的基本概念、原理和分类方法,帮助读者更好地理解和应用机器学习技术。 一、机器学习的基本概念 机器学习是一种通过从数据中学…...
linux控制台命令
进入root sudo su root 浏览当前文件夹列表 ll ls 查看文件 vim test.txt :q 退出查看模式 上传 sudo rz rz 覆盖上传 rz -y 修改文件名: mv 旧文件名 新文件名 修改文件权限 sudo chmod ar xxx.txt sudo chmod 777 test.txt 7 4 2 1 读写运行权限…...
快时尚品牌Halara登上TikTok美国小店榜Top 5,运动健身风靡TikTok
TikTok Shop美国电商数据周榜(11/06-12)已出,具体信息如下: 上周总GMV达到5850万美元,日均出单840万美元;单日出单最高达2110万美元,是当前美国单日最高销售额; 截至11月12日&…...
Docker 安装 Oracle Database 23c
目录 访问 Oracle 官方网站 使用 Docker 运行 Oracle Database 23c 免费容器映像 创建并运行 Oracle Database 23c 容器 查看已下载的镜像 列出正在运行的容器 进入容器 sqlplus 命令 访问 Oracle 官方网站 Database Software Downloads | Oracle 中国 使用 Docker 运行…...
什么是美国服务器,有哪些优势,适用于什么场景?
在互联网发展的过程中,服务器扮演着至关重要的角色。而美国作为全球信息技术的中心,其服务器在全球范围内受到广泛关注。 美国服务器是指在美国本土机房搭建并运行的服务器。其拥有带宽大、优质硬件、售后运维好、位置优越、数据安全性高以及免备…...
TeXLive 2023安装教程
TeXLive 2023安装教程 本文介绍最新TeX发行版——TeXLive 2023的安装步骤。如果你想用LaTeX进行写作,那么需要搭建LaTeX环境:可以选择下面两种方案之一进行安装:(1)TeXLive 2023TeXStudio或者(2)TeXLive 2023WinEdt 11。其中TeXLive 2023是由…...
uniapp中swiper 轮播带左右箭头,点击切换轮播效果demo(整理)
可以点击箭头左右切换-进行轮播 <template><view class"swiper-container"><swiper class"swiper" :current"currentIndex" :autoplay"true" interval"9000" circular indicator-dotschange"handleSw…...
网络连接Android设备
参考:https://blog.csdn.net/qq_37858386/article/details/123755700 二、网络adb调试开启步骤 1、把Android平板或者手机WiFi连接到跟PC机子同一个网段的网络,在设置-系统-关于-状态 下面查看设备IP,然后查看PC是否可以ping通手机的设备的IP。 2、先…...
Redis(位图Bitmap和位域Bitfield)
位图: 位图是字符串类型的扩展。 Redis中的位图是一种特殊的数据结构,用于表示一系列位的集合。它可以存储大量的布尔值数据,每个位代表一个布尔值(0或1),并且可以对这些位进行各种位运算操作。位图通常用…...
【ArcGIS】批量对栅格图像按要素掩膜提取
要把一张大的栅格图裁成分省或者分县市的栅格集,一般是用ArcGIS里的按掩膜提取。 但是有的时候所要求的栅格集量非常大,所以用代码来做批量掩膜(按字段)会非常方便。 import arcpy , shutil , os from arcpy import env from ar…...
二进制安装minio 并实现主从同步
二进制安装minio 并实现主从同步 一、安装部署minio1.1、创建minio目录并下载minio1.2、授予执行权限1.3、创建存储目录和日志目录1.4、在目录下创建一个启动脚本1.5、设置minio开机启动 二、minio主从配置2.1、从服务器安装过程同《一》2.2、从服务器下载mc2.3、配置同步2.4、…...
React中封装echarts图表组件以及自适应窗口变化
文章目录 前言环境代码接口使用效果后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:react.js 🐱👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,…...
鸿蒙:使用Stack、ContentTable、Flex等组件和布局实现一个显示界面
效果展示 一.概述 跟随官网继续HarmonyOS学习 本篇博文实现一个食物详情页的开发Demo 通过这个开发过程学习如何使用容器组件Stack、Flex和基本组件Image、Text,构建用户自定义组件,完成图文并茂的食物介绍 二.构建Stack布局 1.食物名称 创建Stack…...
3.生成验证码 + 开发登录、退出功能 + 显示登录信息
目录 1.生成验证码 2.开发登录、退出功能 2.1 开发数据访问层 2.2 开发业务层:实现登录功能...
基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码
基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于龙格-库塔算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于龙格-库塔优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要:针对P…...
2022最新版-李宏毅机器学习深度学习课程-P51 BERT的各种变体
之前讲的是如何进行fine-tune,现在讲解如何进行pre-train,如何得到一个pre train好的模型。 CoVe 其实最早的跟预训练有关的模型,应该是CoVe,是一个基于翻译任务的一个模型,其用encoder的模块做预训练。 但是CoVe需要…...
JavaFX中Application、Stage、Scene和Parent的区别
在JavaFX中,Application、Stage、Scene和Parent是用于构建图形用户界面(GUI)的关键组件,它们各自有不同的作用和责任。以下是它们之间的主要区别: 1、Application(应用程序) Application是Java…...
ubuntu18.04 terminal打不开的解决方法
目录 现象解决 现象 打开terminal时,一直转圈,然后消失,总是打不开terminal. 解决 编辑文件sudo vim /etc/default/locale,修改为 # File generated by update-locale LANG"en_US.UTF-8" LANGUAGE"en_US:en"重启系统,问题解决....
部署Kubernetes Dashboard
Dashboard简介 Dashboard 是基于网页的 Kubernetes 用户界面。 可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中,也可以对容器应用排错,还能管理集群资源。 Dashboard创建 #创建pods kubectl apply -f https://raw.githubusercontent.com/kub…...
基于LLM与向量数据库构建个人知识管理智能代理:从原理到实践
1. 项目概述:一个面向个人知识管理的智能代理 最近在折腾个人知识管理(PKM)系统,发现了一个挺有意思的开源项目: lessthanno/engram-agent 。简单来说,这是一个“记忆代理”,它旨在成为你数字…...
代购必备:这 5 款工具让你效率翻倍
工具一:跨境电商管理系统 做跨境代购,工具用得好,效率能翻倍。今天分享 5 款我最常用的工具。 这是最核心的工具。好的系统帮你解决: 自动采购:复制链接自动下单仓储管理:商品入库、质检、出库物流追踪&…...
基于Dify低代码平台构建红队自动化工作流:从AI赋能到实战部署
1. 项目概述与核心价值 最近在整理红队自动化工具链时,我深度体验了 din4e/DifyDSL4RedTeam 这个项目。简单来说,这是一个基于 Dify 低代码平台构建的、专门为红队场景设计的自动化工作流集合。在 AI 大模型能力日益渗透到安全领域的今天,…...
教育科技公司利用多模型API为学生提供个性化学习辅导方案
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 教育科技公司利用多模型API为学生提供个性化学习辅导方案 对于教育科技公司而言,构建一个能够理解并解答从小学数学到大…...
在CATLASS样例工程使用AscendC算子调测API
在CATLASS样例工程使用AscendC算子调测API 【免费下载链接】catlass 本项目是CANN的算子模板库,提供NPU上高性能矩阵乘及其相关融合类算子模板样例。 项目地址: https://gitcode.com/cann/catlass AscendC算子调测API是AscendC提供的调试能力,可进…...
Taotoken用量看板如何帮助项目管理者精细化控制AI成本
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken用量看板如何帮助项目管理者精细化控制AI成本 对于项目管理者而言,将大模型能力集成到产品中,除了…...
Argo CD实战指南:基于GitOps的Kubernetes持续交付核心原理与生产级部署
1. 项目概述:为什么我们需要Argo CD?在云原生和微服务架构成为主流的今天,应用部署的复杂性与日俱增。一个典型的应用可能由十几个甚至几十个微服务组成,每个服务都有自己的配置、镜像版本和依赖关系。传统的部署方式,…...
CANN/sip Sinc插值算子
rsInterpolationBySinc 【免费下载链接】sip 本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库,基于华为Ascend AI处理器,专门为信号处理领域而设计。 项目地址: https://gitcode.com/cann/sip 产品支持情况 产品是否支持Atlas 200I…...
AI Agent时代,向量数据库的角色正在悄然重构
在构建复杂多步Agent工作流的生产环境中,我最近反复踩到一个坑:模型能生成规划,工具调用也顺畅,但执行几轮后决策就开始漂移,自我纠正能力迅速衰减。日志一查,问题出在检索层——它还是那个经典RAG的“一次…...
CANN可变长FlashAttentionV2
aclnnFlashAttentionVarLenScoreV2 【免费下载链接】ops-transformer 本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-transformer 产品支持情况 产品是否支持Ascend 950PR/Ascend 950DT√…...
