当前位置: 首页 > 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的[伪]批量新增的问题&…...

数据库面试

1. 简单介绍一下Spring中的事务管理。 答&#xff1a;事务就是对一系列的数据库操作&#xff08;比如将insert&#xff0c;delete&#xff0c;update&#xff0c;select多条sql语句&#xff09;作为一个整体执行&#xff0c;进行统一的提交或回滚操作&#xff0c;如果这组sql语…...

探索Web Components

title: 探索Web Components date: 2024/6/16 updated: 2024/6/16 author: cmdragon excerpt: 这篇文章介绍了Web Components技术&#xff0c;它允许开发者创建可复用、封装良好的自定义HTML元素&#xff0c;并直接在浏览器中运行&#xff0c;无需依赖外部库。通过组合HTML模…...

摄影师在人工智能竞赛中与机器较量并获胜

摄影师在人工智能竞赛中与机器较量并获胜 自从生成式人工智能出现以来&#xff0c;由来已久的人机大战显然呈现出一边倒的态势。但是有一位摄影师&#xff0c;一心想证明用人眼拍摄的照片是有道理的&#xff0c;他向算法驱动的竞争对手发起了挑战&#xff0c;并取得了胜利。 迈…...

CMU最新论文:机器人智慧流畅的躲避障碍物论文详细讲解

CMU华人博士生Tairan He最新论文&#xff1a;Agile But Safe: Learning Collision-Free High-Speed Legged Locomotion 代码开源&#xff1a;Code: https://github.com/LeCAR-Lab/ABS B站实际效果展示视频地址&#xff1a;bilibili效果地址 我会详细解读论文的内容,让我们开始吧…...

Spring中自定义注解进行类方法增强

说明 说到对类方法增强&#xff0c;第一时间想到自定义注解&#xff0c;通过aop切面进行实现。这是一种常用做法&#xff0c;但是在某些场景下&#xff0c;如开发公共组件&#xff0c;定义aop切面可能不是最优方案。以后通过原生aop方式&#xff0c;自定义注解&#xff0c;对类…...

TS:元组

问: 解释下什么是元组 回答: 元组&#xff08;Tuple&#xff09;是一种数据结构&#xff0c;类似于数组&#xff0c;但与数组不同的是&#xff0c;元组中的元素类型可以各不相同&#xff0c;且元组的长度是固定的。元组在许多编程语言中都有实现&#xff0c;包括 TypeScript…...

微服务 | Springboot整合Dubbo+Nacos实现RPC调用

官网&#xff1a;Apache Dubbo 随着互联网技术的飞速发展&#xff0c;越来越多的企业和开发者开始关注微服务架构。微服务架构可以将一个大型的应用拆分成多个独立、可扩展、可维护的小型服务&#xff0c;每个服务负责实现应用的一部分功能。这种架构方式可以提高开发效率&…...

读书的意义

...

第66集《摄大乘论》

请大家打开《讲义》第二二二页&#xff1a; 庚九、念(分二&#xff1a;辛一正念法身&#xff1b;辛二兼显净土) 辛一、正念法身(分二&#xff1a;壬一征&#xff1b;壬二释) 壬一、征 这个是讲到十门分别(二0三页)&#xff0c;分别清净法身的第九段&#xff0c;讲到念&…...

VMware 桥接网络突然无法上网

VMware 桥接网络突然无法上网 0. 问题1. 解决方法 0. 问题 昨天&#xff0c;VMware 桥接网络正常使用&#xff0c;今天突然无法上网。 1. 解决方法 打开VMware的虚拟网络编辑器&#xff0c;将桥接模式的网络从“自动”改成你要使用的网卡&#xff0c;问题解决。 完成&#…...

wordpress无法发邮件/北京网优化seo公司

看过去&#xff0c;历史的尘埃与沧海桑田 古语有云“近代中国&#xff0c;湖南独撑半边天”&#xff0c;湖南长沙&#xff0c;作为湖南省的省会&#xff0c;自古以来便是各界风云人士兴起之地。随着互联网时代的到来&#xff0c;长沙&#xff0c;这座历史悠久的文化名城&…...

学做网站的书籍/百度云资源链接分享群组

时间&#xff1a;2014.04.29 地点&#xff1a;基地二楼 ---------------------------------------------------------------------------------------------- 一、题目 定义字符串的左旋转操作&#xff1a;把字符串前面的若干个字符移动到字符串的尾部。如把字符串abcdef左旋转…...

昆明网站建设咨询/百度链接提交工具

1.安装JSEncrypt npm install jsencrypt2.在登陆页面引用 import { JSEncrypt } from "jsencrypt";3.点击登录对表单验证 // 点击登录按钮async loginClick(){this.$refs.loginFormRef.validate(async valid>{if(!valid){return}// 深拷贝表单对象const form…...

wordpress里面的附件如何导出/百度学术查重

AOP为Aspect OrientedProgramming的缩写&#xff0c;意为面向切面编程。那什么又是面向切面&#xff1f;它与仅有一字之差的OOP又有着什么样的区别与联系&#xff1f;所谓的面向切面编程其实是对业务逻辑又进行了进一步的抽取&#xff0c;将多种业务逻辑中的公用部分抽取出来做…...

网站建设需求确认书/seo外链建设的方法有

译自&#xff1a;How Can The Checkpoints In The Extract Checkpoint File Be Changed? (文档 ID 964684.1)问题&#xff1a; 如何改变抽取进程检查点文件中的检查点&#xff1f; 解决概览&#xff1a; 抽取进程的检查点可以通过拷贝然后在新的检查点文件中改变检查点值来改变…...

做门户网站用什么服务器/湛江百度seo公司

HttpClient 1、get 有参 // 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的) CloseableHttpClient httpClient HttpClientBuilder.create().build();// 1、有参数-拼接 2、无参直接过StringBuffer params new StringBuffer();try …...