当前位置: 首页 > news >正文

深入理解指针(四)

目录

1. 回调函数是什么?

​2. qsort使用举例

2.1冒泡排序

2.2使用qsort函数排序整型数据

​2.3 使用qsort排序结构数据(名字)

2.4 使用qsort排序结构数据(年龄)

3. qsort函数的模拟实现


1. 回调函数是什么?

回调函数就是⼀个通过函数指针调⽤的函数。

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

上述图片中的代码是之前计算器的代码,这种代码比较冗余,我们可以把重复的代码写成一个函数。

#include <stdio.h>void menu()
{printf("**************************\n");printf("*****  1.Add  2.Sub  *****\n");printf("*****  3.Mul  4.Div  *****\n");printf("*****  0.Exit        *****\n");printf("**************************\n");
}int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}
void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int index = 0;printf("请输入两个操作数:>");scanf("%d %d", &x, &y);index = pf(x, y);printf("%d\n", index);
}
int main()
{int input = 0;do{menu();//菜单printf("请输入:>");scanf("%d", &input);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;}} while (input);return 0;
}

输出结果:

2. qsort使用举例

qsort是C语言中的一个库函数,这个函数是用来对任意数据进行排序。

2.1冒泡排序

我们之前学习过一种排序,叫冒泡排序,冒泡排序的思想就是两两比较。

#include <stdio.h>
void bubble_sort(int* arr, int sz)
{for (int i = 0; i < sz - 1; i++){for (int j = 0; j < sz - i - 1; j++){if (*(arr + j) > *(arr + j + 1)){int tem = *(arr + j);*(arr + j) = *(arr + j + 1);*(arr + j + 1) = tem;}}}
}
void print_arr(int* arr, int sz)
{for (int i = 0; i < sz; i++){printf("%d ", *(arr + i));}
}
int main()
{int arr[] = { 2,6,3,4,1,9,10,8,7,5 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);print_arr(arr, sz);return 0;
}

这种代码的功能比较单一,只能排序整型,如果想要排序浮点类型的数据或者其它类型的数据,函数的参数就比如重新设计了,而qsort函数能排序任何类型的数据。

2.2使用qsort函数排序整型数据

qsort:

qsort - C++ Reference

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

 所以qsort的第四个参数就是一个函数,让我们传一个比较大小的函数进去即可,比如我现在想要比较结构体的数据,那么我就需要往进传一个两个结构体成员比较大小的函数,这样qsort就能实现想要排序什么数据就可以排序什么数据。

排序整型的代码:

#include <stdio.h>
#include <stdlib.h>
/*
p1指向的元素大于p2指向的元素就返回大于0的数字
p1指向的元素小于p2指向的元素就返回小于0的数字
p1指向的元素等于p2指向的元素就返回0
*/
//排序整型数据就得提供两个整型的比较函数
int cpm_int(const void*p1, const void*p2)
{//void*的指针不能解引用,必须强转,比较整型那就强转为int*return *(int*)p1 - *(int*)p2;
}int main()
{int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cpm_int);for (int i = 0; i < 10; i++)printf("%d ", *(arr + i));return 0;
}

输出结果:

2.3 使用qsort排序结构数据(名字)

使用qsort排序结构体之前,先看一个操作符,->操作符。

#include <stdio.h>
struct Stu
{char name[20];int age;float light;
};
/*
结构体成员访问操作符.  结构体变量.成员名-> 结构体指针->成员名
*/
int main()
{struct Stu s = { "lixiangsi",20,170.0f };//得到结构体的变量名就用.操作符找结构体的每个成员printf("%s %d %f\n", s.name, s.age, s.light);//那如果现在得到的是结构体的地址呢?struct Stu* ps = &s; //struct Stu*是结构体指针类型//当得到结构体地址的时候,解引用再用.操作符找结构体每个成员printf("%s %d %f\n",(*ps).name, (*ps).age,(*ps).light);//->操作符的使用方法 得到结构体的地址也可以使用->操作符找到结构体的每个成员 printf("%s %d %f\n",ps->name,ps->age,ps->light);return 0;
}

输出结果:

排序结构体数据代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{char name[20];int age;float light;
};
int cmp_name(const void*p1, const void*p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//strcmp这个函数中p1大于p2返回1,p1小于p2返回-1,p1等于p2返回0//刚好是我们需要的返回值,所以将strcmp的结果直接返回就行
}int main()
{struct Stu s[] = { {"zhangsan",20,181.3f},{"lisi",18,175.5f},{"wangwu",25,178.8f} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_name);for (int i = 0; i < sz; i++)printf("%s %d %f\n", s[i].name, s[i].age, s[i].light);return 0;
}

代码运行结果:

 从运行结果可以看出按名字排序,lisi最小,wangwu次之,zhangsan最大,因为lisi的首字母l的ASCILL码值最小,w次之,z最大。

关于字符串怎么比较大小的方式可参考文章字符函数和字符串函数:字符函数和字符串函数-CSDN博客文章浏览阅读577次,点赞27次,收藏13次。在编程的过程中,我们经常要处理字符,为了⽅便操作字符,C语⾔标准库中提供了 ⼀系列操作字符的库函数。https://blog.csdn.net/m0_74271757/article/details/139031604?spm=1001.2014.3001.5501

参考文章中的6. strcmp 的使用和模拟实现章节

2.4 使用qsort排序结构数据(年龄)

#include <stdio.h>
#include <stdlib.h>
struct Stu
{char name[20];int age;float light;
};
int cmp_age(const void* p1, const void* p2)
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;
}int main()
{struct Stu s[] = { {"zhangsan",20,181.3f},{"lisi",18,175.5f},{"wangwu",25,178.8f} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_age);for (int i = 0; i < sz; i++)printf("%s %d %f\n", s[i].name, s[i].age, s[i].light);return 0;
}

输出结果:

我们可以看到qsort函数默认排的是升序,那怎么降序呢?

我们只需要将我们的比较大小的函数返回值颠倒一下就可以,比如说第一个数比第二个数大,本来返回大于0的时候,我们返回小于0的数字,第一个数比第二个数小,本来返回小于0的数字,我们返回大于0的数字即可。

升序前面的大于后面的返回大于0的数字,就交换,现在降序的话前面的大于后面的就不需要交换,所以我们返回小于0的数字,相反,升序的话 前面小于后面的就不交换,但是降序的话前面小于后面的就需要交换,就返回大于0的数字。

3. qsort函数的模拟实现

我们可以将我们的冒泡排序函数bubble_sort函数改造成通用的算法,可以排序任意类型。

模仿qsort函数,只不过qsort底层用的是快速排序算法,而我们用冒泡排序算法实现。

函数参数以及返回值的设计:

 函数体的设计:

交换元素的代码设计:

my_qsort排序整型代码:

#include <stdio.h>
int cmp(const void* p1, const void* p2)
{return *(int*)p1 - *(int*)p2;
}void swap(char* buf1, char* buf2,int size)
{for (int i = 0; i < size; i++){char tem = *((char*)buf1 + i);*((char*)buf1 + i) = *((char*)buf2 + i);*((char*)buf2 + i) = tem;}
}void my_qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*))
{for (int i = 0; i < num - 1; i++){for (int j = 0; j < num - i - 1; j++){if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0){swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int main()
{int arr[10] = { 8,2,6,4,3,7,9,1,5,10 };int sz = sizeof(arr) / sizeof(arr[0]);my_qsort(arr, sz, sizeof(arr[0]), cmp);for (int i = 0; i < sz; i++)printf("%d ", *(arr + i));return 0;
}

输出结果:

my_qsort函数排序结构体代码:

#include <stdio.h>
struct Stu
{char name[20];int age;float light;
};
int cmp_age(const void* p1, const void* p2)
{return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age;
}void swap(char* buf1, char* buf2,int size)
{for (int i = 0; i < size; i++){char tem = *((char*)buf1 + i);*((char*)buf1 + i) = *((char*)buf2 + i);*((char*)buf2 + i) = tem;}
}void my_qsort(void* base, size_t num, size_t size, int (*compar)(const void*, const void*))
{for (int i = 0; i < num - 1; i++){for (int j = 0; j < num - i - 1; j++){if (compar((char*)base + j * size, (char*)base + (j + 1) * size) > 0){swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int main()
{struct Stu s[] = { {"zhangsan",20,167.0f},{"lisi",22,175.5f},{"wangwu",25,180.0f} };int sz = sizeof(s) / sizeof(s[0]);my_qsort(s, sz, sizeof(s[0]), cmp_age);for (int i = 0; i < sz; i++)printf("%s %d %f\n", s[i].name,s[i].age,s[i].light);return 0;
}

输出结果:

泛型编程里面大多数都是void*的指针。

qsort就是典型的用了回调函数的场景。

相关文章:

深入理解指针(四)

目录 1. 回调函数是什么? ​2. qsort使用举例 2.1冒泡排序 2.2使用qsort函数排序整型数据 ​2.3 使用qsort排序结构数据(名字) 2.4 使用qsort排序结构数据(年龄) 3. qsort函数的模拟实现 1. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…...

k-means聚类模型的优缺点

一、k-means聚类模型的优点 1. 简单高效&#xff1a;k-means算法思想简单直观&#xff0c;易于实现。它通过迭代计算样本点与聚类中心之间的距离&#xff0c;并不断调整聚类中心的位置&#xff0c;直至满足终止条件。由于其计算过程相对直接&#xff0c;所以具有较高的执行效率…...

我的创作纪念日(1825天)

Ⅰ、机缘 1. 记得是大一、大二的时候就听学校的大牛说&#xff0c;可以通过写 CSDN 博客&#xff0c;来提升自己的代码和逻辑能力&#xff0c;虽然即将到了写作的第六个年头&#xff0c;但感觉这句话依旧受用; 2、今年一整年的创作都没有停止&#xff0c;本年度几乎是每周都来…...

Studio One 6.6.2 for Mac怎么激活,有Studio One 6激活码吗?

如果您是一名音乐制作人&#xff0c;您是否曾经为了寻找一个合适的音频工作站而苦恼过&#xff1f;Studio One 6 for Mac是一款非常适合您的MacBook的音频工作站。它可以帮助您轻松地录制、编辑、混音和发布您的音乐作品。 Studio One 6.6.2 for Mac具有直观的界面和强大的功能…...

Windows搭建nacos集群

Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c;在国内受欢迎程度较高。 下载地址&#xff1a;Tags alibaba/nacos GitHub 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;8888 解压文件夹 目录说明&am…...

kotlin 中的字符

一、字符类型 1、kotlin中&#xff0c;字符用Char类型表示&#xff0c;值使用单引号 括起来。 fun main() {val a: Char 1println(a) // 1println("a类型为&#xff1a;${a.javaClass.simpleName}") // a类型为&#xff1a;char } 2、特殊字符的表示。 \t——制…...

yocto根文件系统如何配置静态IP地址

在Yocto根文件系统中配置静态IP地址&#xff0c;你可以参考以下步骤。请注意&#xff0c;这些步骤可能会因Yocto版本和具体硬件平台的不同而略有差异。 1. 获取网络配置信息 首先&#xff0c;你需要从网络运维方获取分配的IP地址、子网掩码、默认网关和DNS信息。 2. 确定配置文…...

【博客720】时序数据库基石:LSM Tree的辅助优化

时序数据库基石&#xff1a;LSM Tree的辅助优化 场景&#xff1a; LSM Tree其实本质是一种思想&#xff0c;而具体是否需要WAL&#xff0c;内存表用什么有序数据结构来组织&#xff0c;磁盘上的SSTable用什么结构来存放&#xff0c;是否需要布隆过滤器来加快不存在数据的判断等…...

C++前期概念(重)

目录 命名空间 命名空间定义 1. 正常的命名空间定义 2. 命名空间可以嵌套 3.头文件中的合并 命名空间使用 命名空间的使用有三种方式&#xff1a; 1:加命名空间名称及作用域限定符&#xff08;::&#xff09; 2:用using将命名空间中某个成员引入 3:使用using namespa…...

Java字符串加密HMAC-SHA1密钥,转换成Base64编码

新建一个maven测试项目&#xff0c;直接把代码复制过去就行&#xff0c;把data和secretKey的值替换成想加密的值。 package test;import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchA…...

【网络架构】Nginx

目录 一、I/O模型 1.1 Linux 的 I/O 1.2 零拷贝技术 1.3 网络IO模型 1.3.1 阻塞型 I/O 模型&#xff08;blocking IO&#xff09;​编辑 1.3.2非阻塞型 I/O 模型 (nonblocking IO)​编辑 1.3.3 多路复用 I/O 型 ( I/O multiplexing )​编辑 1.3.4 信号驱动式 I/O 模型 …...

C# OpenCvSharp 逻辑运算-bitwise_and、bitwise_or、bitwise_not、bitwise_xor

bitwise_and 函数 🤝 作用或原理: 将两幅图像进行与运算,通过逻辑与运算可以单独提取图像中的某些感兴趣区域。如果有掩码参数,则只计算掩码覆盖的图像区域。 示例: 在实际应用中,可以用 bitwise_and 来提取图像中的某些部分。例如,我们可以从图像中提取出一个特定的颜…...

JVM常用概念之扁平化堆容器

扁平化堆容器是OpenJDK Valhalla 项目提出的&#xff0c;其主要目标为将值对象扁平化到其堆容器中&#xff0c;同时支持这些容器的所有指定行为&#xff0c;从而达到不影响原有功能的情况下&#xff0c;显著减少内存空间的占用&#xff08;理想条件下可以减少24倍&#xff09;。…...

python面试题5:浅拷贝和深拷贝之间有什么区别?(难度--中等)

文章目录 题目回答1.浅拷贝2.深拷贝 题目 浅拷贝和深拷贝之间有什么区别&#xff1f; 回答 1.浅拷贝 浅拷贝对于不可变数据&#xff0c;如字符串&#xff0c;整数&#xff0c;数组&#xff0c;往往是直接复制其的值。对于可变对象如列表&#xff0c;则是指向同一个地址。这…...

Jetson Linux 上安装ZMQ

1. 安装ZMQ 框架 apt-get install libzmq3-dev 2. 或者自己build ZMQ https://github.com/zeromq/libzmq.git 参考官网教程 3. 安装CPPZMQ CPPZMQ 是ZMQ 的友好的C封装&#xff0c;只需要一个zmq.hpp 头文件即可 git clone https://github.com/zeromq/cppzmq.git cd cppz…...

【Pycharm】设置双击打开文件

概要 习惯真可怕。很多小伙伴用习惯了VsCode开发&#xff0c;或者其他一些开发工具&#xff0c;然后某些开发工具是单击目录文件就能打开预览的&#xff0c;而换到pycharm后&#xff0c;发现目录是双击才能打开预览&#xff0c;那么这个用起来就特别不习惯。 解决办法 只需一…...

Web前端后端架构:构建高效、稳定与可扩展的互联网应用

Web前端后端架构&#xff1a;构建高效、稳定与可扩展的互联网应用 在构建互联网应用的过程中&#xff0c;Web前端与后端架构的设计与实施至关重要。一个优秀的架构能够确保应用的稳定性、高效性和可扩展性&#xff0c;为用户提供流畅、安全的体验。本文将从四个方面、五个方面…...

数据仓库核心:事实表深度解析与设计指南

文章目录 1. 引言1.1基本概念1.2 事实表定义 2. 设计原则2.1 原则一&#xff1a;全面覆盖业务相关事实2.2 原则二&#xff1a;精选与业务过程紧密相关的事实2.3 原则三&#xff1a;拆分不可加事实为可加度量2.4 原则四&#xff1a;明确声明事实表的粒度2.5 原则五&#xff1a;避…...

Reactor和epoll

Reactor模式和epoll都是与事件驱动的网络编程相关的术语&#xff0c;但它们属于不同的概念层面&#xff1a; Reactor模式 Reactor模式是一种事件驱动的编程模型&#xff0c;用于处理并发的I/O事件。这种模式使用一个或多个输入源&#xff08;如套接字&#xff09;&#xff0c…...

Mybatis-Plus多种批量插入方案对比

背景 六月某日上线了一个日报表任务&#xff0c;因是第一次上线&#xff0c;故需要为历史所有日期都初始化一次报表数据 在执行过程中发现新增特别的慢&#xff1a;插入十万条左右的数据&#xff0c;SQL执行耗费高达三分多钟 因很早就听闻过mybatis-plus的[伪]批量新增的问题&…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...