C语言-qsort函数详解

目录

一.qsort函数是什么

 二.使用qsort排序-以升序为例

      关于void*型指针:

1.整形数组排序

2.字符数组排序

3.字符指针数组排序

4.结构体数组排序

5.浮点型数组排序

三.使用冒泡排序思想模拟实现qsort函数

1.什么是冒泡排序:

 2.冒泡排序代码

3. 使用冒泡排序思想模拟实现qsort函数


一.qsort函数是什么

我们可以使用  搜索库函数网址或者MSDN软件进行查找。

qsort()函数:快速排序的函数  -引用stdlib.h头文件

参数说明:
void qsort ( 

    void* base, //要排序的目标数组
    size_t num,     //待排序的元素个数
    size_t width,    //一个元素的大小,单位是字节
    int(*cmp)(const void* e1, const void* e2)

);        

其中cmp是函数指针,cmp指向的是:排序时,用来比较两个元素的函数。需要自己编写。

返回值:

        


 二.使用qsort排序-以升序为例


关于void*型指针:

  void*:无具体类型的指针   能够接收任意类型的地址
 缺点:不能进行运算。不能+-整数,不能解引用

int a  = 0;
float f = 5.5f;
void* p1 = &a;
void* p2 = &f;
p1 = p1+1;    //err

1.整形数组排序

注意:

1.比较函数的参数类型为void* ,我们要进行强制类型转换!且要解引用才能得到对应的值! 

2.若我们想排成降序,只需要写成e2-e1即可

void Print(int* arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", *(arr + i));}printf("\n");
}
//比较整形
//注意类型时void* 所以要强制类型转化,还要解引用才是对应的值!!!
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
void test1()
{int arr[] = { 9,8,7,6,7,5,4,8 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);Print(arr, sz);
}

2.字符数组排序

注意使用sizeof()操作符strlen()函数的区别

//注意要要强制类型转换!! 要解引用!!!  本质上是比较Ascii值
int cmp_char(const void* e1, const void* e2)
{return *(char*)e1 - *(char*)e2;
}
void test4()
{char arr[] ="mango";//若使用sizeof计算长度://int sz = sizeof(arr) / sizeof(arr[0]);	//6//qsort(arr, sz-1, sizeof(arr[0]), cmp_float);//因为sizeof把\0也算进去了,所以计算出来的值比字符串本身长度多1int sz = strlen(arr);	//5qsort(arr, sz, sizeof(arr[0]), cmp_char);printf("%s\n",arr);
}

3.字符指针数组排序

先看看下面这段程序有没有问题?

int cmp_chars(const void* e1, const void* e2)
{return strcmp((char*)e1, *(char*)e2);
}
void test2()
{char* arr1 = "abc";char* arr2 = "wcad";char* arr3 = "cab";char* p[3] = { arr1,arr2,arr3 };int sz = sizeof(p) / sizeof(p[0]);qsort(p, sz, sizeof(p[0]), cmp_chars);int i = 0;for (i = 0; i < sz; i++){printf("%s\n", p[i]);}
}

 打印出来发现:结果是错误的!

 ->调试后发现:e2存放的是p的地址(char**类型),e1存放的是p指向的下一个元素的地址(char**类型)        

对于这种写法,传进去的是p的地址,strcmp()会将p地址对应的内容转化成字符串,也就是将p中arr1,arr2,arr3的地址转化成字符串

实际上应该传p地址空间中arr1,arr2的地址,这样strcmp()才能找到arr1和arr2对应的字符串,因此得先把e1,e2转化成char**,这样解引用以后才是一个char*的地址

原因:把p传给qsort,p是数组名->首元素地址,元素类型为char*>,所以p的类型为:char**类型。  所以e1 和e2也要强制类型转化为char**,解引用e1,e2才是对应字符串的地址!

正解: 

int cmp_chars(const void* e1, const void* e2)
{return strcmp(*(char**)e1, *(char**)e2);
}
void test2()
{char* arr1 = "abc";char* arr2 = "wcad";char* arr3 = "cab";char* p[3] = { arr1,arr2,arr3 };int sz = sizeof(p) / sizeof(p[0]);qsort(p, sz, sizeof(p[0]), cmp_chars);int i = 0;for (i = 0; i < sz; i++){printf("%s\n", p[i]);}

4.结构体数组排序

比较年龄->实际比较的是整形

比较名字->实际比较的是字符串->使用strcmp函数,不能使用 == 判断

struct Stu
{int age;char name[20];
};
//比较结构体中元素的年龄
int cmp_age(const void* e1, const void* e2)
{//本质是比较整形return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//比较名字
int cmp_name(const void* e1, const void* e2)
{//本质是字符串比较->使用strcmp函数return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
void test2()
{//创建结构体数组,用大括号初始化struct Stu s[3] = { {19,"Mango"},{18,"Lemon"},{20,"Hello"} };int sz = sizeof(s) / sizeof(s[0]);//以年龄排qsort(s, sz, sizeof(s[0]), cmp_age);printf("%s %d ",s[0].name,s[0].age);printf("%s %d ", s[1].name, s[1].age);printf("%s %d ", s[2].name, s[2].age);printf("\n");//以姓名排qsort(s, sz, sizeof(s[0]), cmp_name);printf("%s %d ", s[0].name, s[0].age);printf("%s %d ", s[1].name, s[1].age);printf("%s %d ", s[2].name, s[2].age);printf("\n");
}

5.浮点型数组排序

注意:比较函数中,返回类型是int,最后相减的值要强制类型转化为int ,但这也会造成错误,建议使用方法2.

//写法1:可能会出错
// 原因: 0.2 -0.1 = 0.1 强制类型转化为int后 结果为0
//int cmp_float(const void* e1, const void* e2)
//{
//	//返回类型是int  所以相减后的结果要强制类型转化
//	return (int)(*(float*)e1 - *(float*)e2);
//}//写法2:对应上qsort的返回值
int cmp_float(const void* e1, const void* e2)
{if ((*(float*)e1 - *(float*)e2) > 0.00000)return 1;else if ((*(float*)e1 - *(float*)e2) == 0.000000)return 0;elsereturn -1;
}
void test3()
{float arr[5] = { 5.01f,5.01f,0.02f,0.01f,5.001f };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_float);int i = 0;for (i = 0; i < sz; i++){printf("%f ", arr[i]);}
}

三.使用冒泡排序思想模拟实现qsort函数

1.什么是冒泡排序:

在这里插入图片描述

主要思想:相邻的两个元素进行比较 

 

 对于冒泡排序: n个元素 共进行n-1趟冒泡排序。一趟可以使一个元素在特定位置上,每趟排序可以少比较一个元素

但是冒泡排序只能排序整形


 2.冒泡排序代码

void BubbleSort(int* arr, int sz)
{int i = 0;int j = 0;//共进行sz-1趟for (i = 0; i < sz-1; i++){int flag = 1;//每一趟进来都假设有序// 每一趟for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}//若falg还是1,说明没有交换->已经有序了break退出if (flag == 1){break;}}
}
int main()
{int arr[10] = { 2,3,6,7,9,0,0,3,2,10 };int sz = sizeof(arr) / sizeof(arr[0]);BubbleSort(arr, sz);return 0;
}

3. 使用冒泡排序思想模拟实现qsort函数

qsort库函数使用的是什么参数,我们设计的函数就使用什么参数!

  

1.为何将base强制类型转化为char*型指针:

原因:char* 指针+1跳过一个字节,+width:跳过width个字节,指向下一个元素。转化为其他类型不合适

2. 交换函数:还要把宽度(每个元素所占字节数)传过去
因为交换的时候是传地址,所以要知道元素的宽度,一个字节一个字节的交换 ,这样也证明了使用char*指针的好处!

3.(char*)base + j * width, (char*)base + (j + 1) * width,

  当j = 0时:比较的是第一个元素和第二个元素
   j = 1时,比较的是第二个元素和第三个元素
    ....  很妙的写法

//交换 --一个字节一个字节的交换,共交换width次
void Swap(char* buf1, char* buf2, size_t width)
{size_t i = 0;for (i = 0; i < width; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
void my_BubbleSort(void* base, size_t num,size_t width, int(*cmp)(const void* e1, const void* e2))
{//冒泡排序//若要排序n个元素,只需要进行n-1趟//每一趟可以少比较一个元素,每一趟可以使一个元素在确定的位置上//num:要排序元素的个数 类型是size_t //num是无符号数 防止产生警告 所以i和j也定义为size_t// size_t == unsigned int size_t i = 0;size_t j = 0;//共进行num-1趟for (i = 0; i < num; i++){//每一趟for (j = 0; j < num - 1 - i; j++){//比较//传地址   //相邻两个元素比较   width:宽度,每个元素所占字节//排成升序if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0){//交换两数Swap( (char*)base + j * width, (char*)base + (j + 1) * width, width );}}}
}

当然 ,交换也可以使用库函数memcpy

dest:目标空间 

 src:要拷贝到目标空间的字符 -因为不作修改,所以可以用const修饰

count:字节数

char tmp [30];    //防止结构体类型之类的类型    临时空间
memcpy(tmp, (char*)base + j * size, size); 
memcpy( (char*)base + j * size,  (char*)base + (j + 1) * size, size);
memcpy( (char*)base + (j + 1) * size, tmp, size);

 如果文章对你有帮助的,欢迎大佬们点个赞留言呀!如果有错误的话,请评论告知!

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C语言qsort()函数的使用(详解)

目录 1.参数含义 1.首元素地址base 2.元素个数num 3.元素大小size 4.自定义比较函数compar 2.使用方式 1.头文件 2.compar的实现 3.整体代码 qsort&#xff08;&#xff09;函数&#xff08;quick sort&#xff09;是八大排序算法中的快速排序&#xff0c;能够排序任意…

虚幻4地形怎么增加层_虚幻周报20200407 | 在家也要好好工作呀~

官方知乎号&#xff1a;虚幻引擎 搜集日期&#xff1a;20200330-20200405 整理编辑&#xff1a; 大钊&#xff0c;小辉辉&#xff0c;马古斯&#xff0c;小帅帅 声明&#xff1a;文档搜集来自网上&#xff0c;难免遗漏&#xff0c;请联系我们投稿和添加关注。该文档版权归整理…

2020年笔记本电脑选购指南

**本文首发微信公众号陈蛋蛋碎碎念&#xff0c;获取更多软件、教程、模板资源&#xff0c;请关注公众号。 最近在群里有小伙伴问关于选购电脑的事&#xff0c;毕竟开学季又要到了。刚好蛋蛋现在又在3C公司工作&#xff0c;所以平时也需要了解这方面的知识&#xff0c;于是就准…

[转]Warzone 2100(战争地带2100)

发行公司&#xff1a;Eidos Interactive 开发公司&#xff1a;Pumpkin Studios 游戏类型&#xff1a;即时战略 游戏语言&#xff1a;英文 发行日期&#xff1a;1999 年4月 系统操作&#xff1a;Win95以上 伴随着这部来自英国开发公司Pumpkin Studios的作品&#xff0c;1999年…

显卡优化软件测试面试,是吹嘘还是真有用?NV游戏优化软件测试

1前言&#xff1a;NVIDIA游戏优化软件试用 英伟达真实热衷为用户提供出色的游戏方案&#xff0c;日前发布了一款GeForce Experience的新软件&#xff0c;本质上是一个基于云端的服务&#xff0c;旨在分析你的硬件并自动调整显示分辨率和游戏设置&#xff0c;以提供更好更优化的…

【转】为什么中国不会有3A游戏

编&#xff1a;王妙一&#xff0c;游戏开发者&#xff0c;代表作《WILL&#xff1a;美好世界》&#xff0c;曾获第二届PlayStation开发者大赛冠军。王妙一大学时就读于清华软件学院图形研究所&#xff0c;本文将从游戏图形技术的发展变迁开始&#xff0c;谈谈3A游戏在现今&…

FPS游戏

FPS 求助编辑百科名片 FPS FPS是第一人称射击类游戏的简称&#xff08;游戏专有名词&#xff09;。 FPS&#xff08;First-Person Shooter Game&#xff09;&#xff1a;第一人称射击游戏 严格来说FPS属于ACT类游戏的一个分支&#xff0c;但和RTS类游戏一样&#xff0c;由于其在…

Centos6.5环境Nginx 1.16.1升级到1.24.0版本

一、背景 2023年4月11日&#xff0c;官方发布了Nginx最新稳定版&#xff0c;版本号为 1.24.0。该版本是基于1.23.x&#xff08;1.23.0 - 1.23.4&#xff09;开发版的Bug修复&#xff0c;以及一些新特性的加入&#xff0c;而形成的稳定版。安全部门扫描后&#xff0c;发现现场不…

C++11 -- 包装器

文章目录 function包装器function包装器的概念function的运用function实例化使用function解决逆波兰表达式 bind包装器bind包装器相关介绍bind绑定函数固定参数 function包装器 function包装器的概念 function包装器,也叫做适配器,它的本质是一个类模板. 例如: 1 template&l…

chatgpt赋能python:Python中撤销的快捷键

Python中撤销的快捷键 在编程中&#xff0c;我们经常需要进行调试&#xff0c;不可避免地会出现一些错误&#xff0c;这时候撤销 (Undo) 功能就显得尤为重要。在 Python 中&#xff0c;我们可以使用一些快捷键来快速撤销&#xff0c;本文将会介绍这些快捷键的使用以及使用它们…

chatgpt赋能python:Python中的构造函数

Python 中的构造函数 Python 是一门广泛应用于各种应用领域的高级编程语言&#xff0c;它支持不同的编程范式&#xff0c;包括面向对象编程。在面向对象编程中&#xff0c;构造函数是一个重要的概念。本文将介绍 Python 中的构造函数&#xff0c;并介绍如何使用它们来创建对象…

淘宝店铺老店标识怎么显示 怎么淘宝老店标识申请

我们在很多时候都喜欢去一个淘宝开的时间比较长的店铺去购买商品&#xff0c;因为这样的店铺可能在信誉度这一块会更加能够让人信服&#xff0c;因为一个店铺能开这么久&#xff0c;肯定还是证明这个店铺拥有一定的实力。淘宝店铺老店标识怎么显示 怎么淘宝老店标识申请 在回答…

22-0001 淘宝店铺搜索界面

淘宝店铺搜索界面 1.元素2.过程2.1 搜索界面的网页源码2.2 通过Chrome控制台获取sellerid2.3 搜索链接2.4 控制台 3.总结 1.元素 获取店铺搜索界面每个店铺的’sellerid’ 备注&#xff1a;通过sellerid可以在下面链接中获取买家秀的图片&#xff0c;也可以使用相关软件进行下…

淘宝开店指南——店铺设置篇

目录 店铺基本设置店铺装修手机店铺装修PC 店铺装修 保证金管理客服&#xff08;子账号&#xff09;管理创建子账号修改子账号权限 店铺基本设置 通过千牛主账号工作台左侧点击【店铺】->选择【店铺信息进入】。 设置链接&#xff1a;点击访问 可设置内容&#xff1a; 个…

淘宝/天猫API:seller_info-获得淘宝店铺详情

万邦淘宝/天猫获得淘宝店铺详情 API 返回值说明 seller_info-获得淘宝店铺详情 onebound.taobao.seller_info 公共参数 请求地址: https://console.open.onebound.cn/console/?ipony 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;…

SpringCloud Alibaba Seata 工作机制

SpringCloud Alibaba Seata Seata 工作机制 说明 之所以放在后面说工作机制是因为如果一开始就说的话理解困难 所以我们有了前面的列子和说明我们在结合本节内容会收获的多理解相对容易点 分布式事务过程分析 Seata 分布式事务处理过程-ID三组件模型 debug 梳理: 术语 先…

ShardingSphere笔记(三):自定义分片算法 — 按月分表·真·自动建表

ShardingSphere笔记&#xff08;二&#xff09;&#xff1a;自定义分片算法 — 按月分表真自动建表 文章目录 ShardingSphere笔记&#xff08;二&#xff09;&#xff1a;自定义分片算法 — 按月分表真自动建表一、 前言二、 Springboot 的动态数据库三、 实现我们自己的动态数…

如何创建springboot项目

SpringBoot 优点 可快速构建spring应用直接嵌入tomcat、jetty、undenrtow服务器&#xff08;无须部署war文件&#xff09;提供依赖启动器&#xff08;starter&#xff09;简化构建配置极大程度的自动化配置Spring和第三方库提供生产就绪功能&#xff0c;例如指标监控检测、外部…

函数(C语言程序设计)

目录 一、函数定义 二、函数调用 三、递归函数 四、局部变量和全局变量 一、函数定义 1、无参函数的定义 类型名 函数名&#xff08;&#xff09; /*函数首部*/ { 函数体 } 或 类型名 函数名&#xff08;void&#xff09; /*函数首部*/ { 函数体 } void类型的函数不…

因为修改系统设置导致edge浏览器打不开的最快解决办法

设置&#xff0d;应用&#xff0d;应用与功能&#xff0c;然后找到Microsoft edge点击修改进行修复&#x1f917;