指针进阶(三)(C 语言)

目录

  • 一、回调函数
  • 二、快速排序函数 qsort
    • 1. qsort() 函数原型
    • 2. 使用 qsort() 函数
  • 三、仿照 qsort 函数设计一个可以排序任意类型数组的冒泡函数
    • 1. 函数原型
    • 2. 函数设计思路

一、回调函数

在 C 语言中,回调函数是一种通过函数指针调用的函数,也就是一个被作为参数传递的函数。在特定的事件或条件发生时,主程序可以调用这个被传递进来的函数来执行特定的操作。

在指针进阶(二)(C 语言)中的最后我们使用函数指针数组模拟了简单的计算器,我们把其中的输入计算部分,封装成了一个函数 calc(),需要传入对应操作的函数地址,然后在 calc() 函数中通过函数指针来调用相应的函数。而这个被调用的函数就被称为回调函数。而在这里使用回调函数的主要作用是减少代码的冗余,如果不使用回调函数,那么 switch 语句部分就会是如下代码:

switch (input){case 1:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}

可以看到上述代码的前四个选项中,除了调用的函数不同,其他代码部分都是相同的。所以,前四个选项的功能设计成一个函数,根据不同的功能传递不同的函数指针,使用回调函数来完成相应的功能。这样减少了代码的冗余,提升了代码的可读性。

switch (input){case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}

二、快速排序函数 qsort

C 语言标准库中的 qsort() 函数是专门用来给数组排序的,且只要给它提供一个合适的比较函数,它可以排序任意类型的数组。

1. qsort() 函数原型

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

可以看到上面函数原型中的第一个参数 base 是 void* 指针,因为该函数预先并不知道你要排序哪种类型的数组,所以只能使用 void* 指针来进行接收待排序数组的首地址。第二个参数 nmemb 的类型是 size_t 用来接收数组元素的个数。第三个参数 size 的类型是 size_t 用来接收数组每个元素的大小,单位字节。最后一个参数 compar 的类型是指向需要两个 const void* 的指针作为参数且返回值为 int 的数组,该指针的作用是回调我们传递的比较函数,因为 qsort 函数预先不知道我们需要比较的类型,可能是 int、double,也可能是字符串或者结构体。

2. 使用 qsort() 函数

接下来分别使用 qsort() 函数来排序 int 数组、double 数组、字符串数组以及结构体数组。需要分别为这四种类型设计四个比较函数:

// 头文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 学生结构体
typedef struct Stu
{char name[20];char age;
}Stu;// int 类型比较
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*) e2;
}// double 类型比较
int cmp_double(const void* e1, const void* e2)
{return *(double*)e1 - *(double*)e2;
}// 字符串类型比较
int cmp_str(const void* e1, const void* e2)
{return strcmp((char*)e1, (char*)e2);
}// Stu 结构比较
int cmp_Stu(const void* e1, const void* e2)
{return (*(Stu*)e1).age - (*(Stu*)e2).age;
}

使用 qsort() 函数需要包含头文件 stdlib.h,使用字符串比较函数 strcmp() 需要包含头文件 string.h。下面是创建所需变量和排序的代码:

int main()
{// int 类型排序int arr_i[10] = { 1,3,5,7,9,2,4,6,8,0 };int sz = sizeof(arr_i) / sizeof(arr_i[0]);printf("排序前: ");print_arr_i(arr_i, sz);printf("排序后: ");qsort(arr_i, sz, sizeof(int), cmp_int);print_arr_i(arr_i, sz);// double 类型排序double arr_d[] = { 1.1,9.9,8.8,3.3,6.6,7.7,4.4,5.5 };sz = sizeof(arr_d) / sizeof(arr_d[0]);printf("排序前: ");print_arr_d(arr_d, sz);printf("排序后: ");qsort(arr_d, sz, sizeof(double), cmp_double);print_arr_d(arr_d, sz);// 字符串类型排序char arr_str[5][20] = {"abcde111","ecdba222","mnbvd333","iknhk444","cvbnm555"};sz = sizeof(arr_str) / sizeof(arr_str[0]);printf("\n排序前:\n");print_arr_str(arr_str, sz);printf("排序后:\n");qsort(arr_str, sz, sizeof(arr_str[0]), cmp_str);print_arr_str(arr_str, sz);// Stu 结构类型排序Stu arr_Stu[3] = {{"张三", 20},{"王五", 19},{"李四", 18}};sz = sizeof(arr_Stu) / sizeof(arr_Stu[0]);printf("\n排序前:\n");print_arr_Stu(arr_Stu, sz);printf("排序后:\n");qsort(arr_Stu, sz, sizeof(arr_Stu[0]), cmp_Stu);print_arr_Stu(arr_Stu, sz);return 0;
}

程序的运行结果如下:
在这里插入图片描述

三、仿照 qsort 函数设计一个可以排序任意类型数组的冒泡函数

1. 函数原型

// 仿照 qsort 函数设计一个可以排序任意类型数组的冒泡函数
void bubble_sort(void* start, size_t n, size_t size, int (*cmp)(const void* e1, const void* e2));

这里需要知道,我们实现的 bubble_sort() 函数和库函数 qsort() 只是排序使用的算法不同,其他的思路都是一样的,所以函数的参数和返回类型也是一样的。

2. 函数设计思路

进入函数 bubble_sort 以后,冒泡排序的整体是不变的,依旧是嵌套循环,外层是排序次数,内层是比较次数。但是由于预先不知道传入数组的元素类型,所以我们面临如下两个问题:
(1)在比较的时候如何传递每个元素的地址
(2)如何交换相邻的元素

首先,我们知道传入数组的首地址以及数组每个元素的大小。那么我们可以把数组首地址强制类型转换为 char* 类型,然后加上数组元素大小的整数倍,那么就可以得到数组的每个元素的地址了。思路如下图:
在这里插入图片描述
所以,就可以写出如下代码:

// 仿照 qsort 函数设计一个可以排序任意类型数组的冒泡函数
void bubble_sort(void* start, size_t n, size_t size, int (*cmp)(const void* e1, const void* e2))
{int i;for (i = 0; i < n - 1; ++i)  // 外层排序 n-1 轮{int j;for (j = 0; j < n - i - 1; ++j)  // 内层比较 n-i-1 次{if (cmp((char*)start + j * size, (char*)start + (j + 1) * size) > 0)  // 排升序{// 交换}}}
}

那么如何交换两个元素?我们知道两个元素的首字节地址,也知道两个元素的大小,那么我们可以通过交换两个元素的每个字节来实现两个元素的交换。思路如下图:
在这里插入图片描述

按理来说应该把这个交换功能设计成一个函数,但是这里为了方便观看,就放在函数内部了:

// 仿照 qsort 函数设计一个可以排序任意类型数组的冒泡函数
void bubble_sort(void* start, size_t n, size_t size, int (*cmp)(const void* e1, const void* e2))
{int i;for (i = 0; i < n - 1; ++i)  // 外层排序 n-1 轮{int j;for (j = 0; j < n - i - 1; ++j)  // 内层比较 n-i-1 次{if (cmp((char*)start + j * size, (char*)start + (j + 1) * size) > 0)  // 排升序{// 交换int k;for (k = 0; k < size; ++k){char tmp = *((char*)start + j * size + k);*((char*)start + j * size + k) = *((char*)start + (j + 1) * size + k);*((char*)start + (j + 1) * size + k) = tmp;}}}}
}

接下来使用前面 qsort() 函数排序的数据检验我们所写的 bubble_sort() 函数,只需要把调用的函数名 qsort 改成 bubble_sort 即可:

int main()
{// int 类型排序int arr_i[10] = { 1,3,5,7,9,2,4,6,8,0 };int sz = sizeof(arr_i) / sizeof(arr_i[0]);printf("排序前: ");print_arr_i(arr_i, sz);printf("排序后: ");bubble_sort(arr_i, sz, sizeof(int), cmp_int);print_arr_i(arr_i, sz);// double 类型排序double arr_d[] = { 1.1,9.9,8.8,3.3,6.6,7.7,4.4,5.5 };sz = sizeof(arr_d) / sizeof(arr_d[0]);printf("排序前: ");print_arr_d(arr_d, sz);printf("排序后: ");bubble_sort(arr_d, sz, sizeof(double), cmp_double);print_arr_d(arr_d, sz);// 字符串类型排序char arr_str[5][20] = {"abcde111","ecdba222","mnbvd333","iknhk444","cvbnm555"};sz = sizeof(arr_str) / sizeof(arr_str[0]);printf("\n排序前:\n");print_arr_str(arr_str, sz);printf("排序后:\n");bubble_sort(arr_str, sz, sizeof(arr_str[0]), cmp_str);print_arr_str(arr_str, sz);// Stu 结构类型排序Stu arr_Stu[3] = {{"张三", 20},{"王五", 19},{"李四", 18}};sz = sizeof(arr_Stu) / sizeof(arr_Stu[0]);printf("\n排序前:\n");print_arr_Stu(arr_Stu, sz);printf("排序后:\n");bubble_sort(arr_Stu, sz, sizeof(arr_Stu[0]), cmp_Stu);print_arr_Stu(arr_Stu, sz);return 0;
}

运行结果和使用 qsort() 函数时是一样的:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot驱动的植物健康监测革命

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理植物健康系统的相关信息成为必然。开发合适…

软件测试学习笔记丨Selenium学习笔记:常用页面信息对比方法expected_conditions

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/22514 本文为霍格沃兹测试开发学社的学习经历分享&#xff0c;写出来分享给大家&#xff0c;希望有志同道合的小伙伴可以一起交流技术&#xff0c;一起进步~ 说明&#xff1a;本篇博客基于sel…

开拓鸿蒙测试新境界,龙测科技引领自动化测试未来

在当今科技舞台上&#xff0c;鸿蒙 OS 以非凡先进性强势登场&#xff0c;打破传统操作系统格局&#xff0c;为软件测试领域带来全新机遇与艰巨挑战。 一、鸿蒙 OS 的辉煌崛起 &#xff08;一&#xff09;壮丽发展历程与卓越市场地位 鸿蒙 OS 的发展如波澜壮阔的史诗。2023 年…

JavaScript进阶:手写代码挑战(一)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;JavaScript篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来JavaScript篇专栏内容:JavaScript手写代码篇 #1024程序员节&#xff5c;征文# 在现代Web开发中&#xff0c…

中国移动机器人将投入养老场景;华为与APUS共筑AI医疗多场景应用

AgeTech News 一周行业大事件 华为与APUS合作&#xff0c;共筑AI医疗多场景应用 中国移动展出人形机器人&#xff0c;预计投入养老等场景 作为科技与奥富能签约&#xff0c;共拓智能适老化改造领域 天与养老与香港科技园&#xff0c;共探智慧养老新模式 中山大学合作中国…

[Python学习日记-53] Python 中的正则表达式模块 —— re

[Python学习日记-53] Python 中的正则表达式模块 —— re 简介 re 模块 练习 简介 我们在编程的时候经常会遇到想在一段文字当中找出电话号码、身份证号、身高、年龄之类的信息&#xff0c;就像下面的数据一样 # 文件名&#xff1a;美丽学姐联系方式.txt 姓名 地区 …

微信小程序美团点餐

引言&#xff1a;外卖已经成为了都市人的必备&#xff0c;在无数个来不及&#xff08;懒得&#xff09;做饭的时刻拯救孤单寂寞的胃。美团外卖无疑是外卖届的领头羊&#xff0c;它的很多功能与设计都值得我们学习。本文将从五个方面&#xff0c;对美团外卖展开产品分析&#xf…

【ArcGIS Pro实操第4期】绘制三维地图

【ArcGIS Pro实操第4期】绘制三维地图 ArcGIS Pro绘制三维地图-以DEM高程为例参考 如何使用ArcGIS Pro将栅格数据用三维的形式进行表达&#xff1f;在ArcGIS里可以使用ArcScene来实现&#xff0c;ArcGIS Pro实现原理跟ArcScene一致。由于Esri未来将不再对ArcGIS更新&#xff0c…

深入浅出神经网络:从基础原理到高级应用

第5章 神经网络 更加详细内容可以看这篇文章 5.1 神经元模型 神经网络的基本单元是神经元模型。神经元模拟了生物神经元的行为&#xff0c;通过接收输入信号&#xff0c;进行加权求和&#xff0c;然后经过激活函数输出结果。 数学上&#xff0c;一个简单的神经元可以表示为&…

pipeline开发笔记

pipeline开发笔记 jenkins常用插件Build Authorization Token Root配置GitLab的webhooks(钩子)配置构建触发器--示例 piblish over sshBlue OceanWorkspace Cleanup PluginGit插件PipelineLocalization: Chinese (Simplified) --中文显示Build Environment Plugin 显示构建过程…

ArcGIS 10.8 安装教程

目录 一、ArcGIS10.8二、安装链接三、安装教程四、ArcGIS实战 &#xff08;一&#xff09;ArcGIS10.8 1. 概述 ArcGIS 10.8是由美国Esri公司开发的GIS平台&#xff0c;用于处理、分析、显示和管理地理数据&#xff0c;并实现数据共享。它具有新特性和功能&#xff0c;性能更…

iOS MPNowPlayingInfoCenter 通知栏、锁屏 显示当前播放的媒体信息

前言 MPNowPlayingInfoCenter 是 iOS 框架 MediaPlayer 中的一个类&#xff0c;主要用于管理锁屏界面、控制中心、通知中心中显示的“当前播放”媒体信息。它允许开发者向用户展示正在播放的音乐或媒体信息&#xff0c;并控制媒体播放。 通过 MPNowPlayingInfoCenter&#xf…

新电脑Win11家庭中文版跳过联网激活方法(教程)

预装Win11家庭中文版的新电脑&#xff0c;如何跳过联网激活&#xff1b;由于微软限制必须要联网激活&#xff0c;需要使用已有的微软账户登入或者注册新的微软账户后才可以继续开机使用&#xff0c;Win11联网后系统会自动激活。下面介绍一下初次开机初始化电脑时如何跳过联网激…

猫咪掉毛还容易应激,哪款宠物空气净化器可以吸毛且低噪?

今年的双十一第一波优惠我没有抢&#xff0c;因为我在犹豫我真的必须要买宠物空气净化器&#xff0c;但是会不会有很多副作用等等问题&#xff0c;让我一直不敢下手。 一直犹豫买不买是因为我家养了一只爱掉毛的小猫咪&#xff0c;家里每天都是想着要清理猫咪掉下来的猫毛&…

又是一年 1024

今天是 1024 程序员节&#xff0c;现在是一名大数据讲师&#xff0c;我和往常一样&#xff0c;依旧在讲课中度过。对于很多程序员来说&#xff0c;这一天也许是属于代码、调试和无数行 SQL 查询的&#xff0c;而对于我来说&#xff0c;虽然工作内容不同&#xff0c;却也和数据、…

软考算法——线性表、栈和队列、串、数组、矩阵和广义表

软考算法&#xff08;一&#xff09; 线性表定义顺序表单链表循环链表双向链表 性能分析线性表插入删除操作 栈和队列栈队列循环队列 串、数组、矩阵和广义表串串的基本操作串的存储结构 数组数组的存储地址计算 矩阵——稀疏矩阵上三角矩阵下三角矩阵 广义表 线性表 定义 线性…

设置K8s管理节点异常容忍时间

说明 每个节点上的 kubelet 需要定时向 apiserver 上报当前节点状态&#xff0c;如果两者间网络异常导致心跳终端&#xff0c;kube-controller-manager 中的 NodeController 会将该节点标记为 Unknown 或 Unhealthy&#xff0c;持续一段时间异常状态后 kube-controller-manage…

软考——计算机网络概论

文章目录 &#x1f550;计算机网络分类1️⃣通信子网和资源子网2️⃣网络拓扑结构3️⃣ 计算机网络分类3&#xff1a;LAN MAN WAN4️⃣其他分类方式 &#x1f551;OSI 和 TCP/IP 参考模型1️⃣OSI2️⃣TCP/IP&#x1f534;TCP/IP 参考模型对应协议 3️⃣OSI 和 TCP/IP 模型对应…

AUTOSAR_EXP_ARAComAPI的6章笔记(4)

☞返回总目录 相关总结&#xff1a;《AUTOSAR 自适应应用中原始数据流传输的使用方法》总结 6.4 原始数据流传输的使用方法 本章描述了原始数据流&#xff08;RawDataStreams&#xff09;在 AUTOSAR 自适应应用程序中的使用方法。 目前&#xff0c;原始数据流传输在单播 / …

WSL2-轻量级AI训练场景最佳生产环境

WSL2 只适用于 Win 10 、Win11 在运行 AI 软件、AI 模型训练&#xff0c;Linux 是最佳的操作系统。 在运行各种软件&#xff0c;如&#xff1a;Stable Diffusion Web UI 等&#xff0c;使用 Docker 容器运行也更方便后期的快速复用&#xff0c;同样的 Docker 容器在 Linux 中…