带你学C语言-指针(4)

目录

​编辑

⚾0.前言

🏀1.回调函数

⚽2.qsort

🏉2.1 qsort函数的模拟实现

🎾3.sizeof与strlen对比

🎾4.结束语


⚾0.前言

        言C之言,聊C之识,以C会友,共向远方。各位CSDN的各位你们好啊,这里是持续分享C语言知识的小赵同学,今天要分享的C语言知识是深入了解指针(4),在这一章,小赵将会和大家继续聊指针的相关内容。✊

🏀1.回调函数

那么首先我们要了解的就是什么是回调函数呢?其实回调函数指的就是我们在使用一个函数的时候,我们讲另一个函数以地址的形式传入这个函数中,然后我们在这个函数中,通过函数指针去调用我们传入的函数,而这个我们传入的函数其实也就是我们回调函数。那么废话不多说它的出现究竟可以给我们的代码带来哪些便捷呢?下面我们看下面这样一个代码。

#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf("*************************\n");printf(" 1:add             2:sub \n");printf(" 3:mul             4:div \n");printf(" 0:exit                  \n");printf("*************************\n");printf("请选择:");scanf("%d", &input);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;}} while (input);return 0;
}

这一个代码 其实也就是我们上一章节时候使用的计算器的代码,在上一章其实我们已经用过我们的函数指针数组对这个代码进行过一次优化,那么这个代码能否用我们的回调函数进行优化呢?那么该如何优化呢其实也就是对那他原本的输入数字的方式稍微改一下。

在这里我们先创建一个函数

void calc(int(*pf)(int, int))
{int ret = 0;int x, y;printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);printf("ret = %d\n", ret);
}

这个其实就相当于二次集装箱,把我们的函数再次进行一次打包,那么我们下面的主函数调用时候只需要讲函数名导入函数中就可以了。就不用分开在一次一次敲输入的代码了。 

int main()
{int input = 1;do{printf("*************************\n");printf(" 1:add             2:sub \n");printf(" 3:mul             4:div \n");printf(" 0:exit                  \n");printf("*************************\n");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函数,这个函数有什么特点,一是实现快速排序,二是它里面使用了我们的回调函数,可以拿来作为我们的训练运用。

可能有人看到这些英文会一阵头疼,但是没关系,如果我们可以看懂它的列子(即知道它的作用)和它的参数就可以完美使用这个函数了。或者我们也可以试着去翻译这个英文,翻译器之类的使用上,实在不行我们就当时恶补英语好了哈哈。好了我们言归正传吗,我们可以看到这个函数它的参数,第一个是我们要排列的数组,第二个是数组的元素数量,第三个则是我们数组中每个元素的大小,最后一个则是比较函数。(这里的排列一定是针对我们元素是一样的,毕竟我们的数组里面的元素就是一样的,不对吗?),这个函数使用上唯一的烦就是我们要自己搞个比较函数,可以说是伤了很多人的脑袋,但实际上也是比较简单的。

(这个是对我们的比较函数的要求即两个数字相减如果为负数则小的在前,大的在后) 

这里举一个整数比较的例子(这里唯一要注意的是这个比较函数的参数类型是const void*)(这里为什么是const void*,因为const void*可以接受任意类型的值)。

#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}

当然我们有的时候要比较的不仅仅是数字还有字母等。

他们要怎么比较呢?这里我们就不得不用我们的字符串函数中的strcmp。

 它的整个比较也是和我们上面的输出其实也是一样的。

int int_cmp(const void* p1, const void* p2)
{return strcmp((char*)p1, (char*)p2);
}

那么我们在要用哪个的时候实现哪个就行,下面我们来演示两组。

#include <stdio.h>
#include<string.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void* p1, const void* p2)
{return strcmp((char*)p1, (char*)p2);
}
int main()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");return 0;
}
struct Stu//结构体后面会聊
{char name[20];int age;
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//结构体指针可以用->来访问结构体内部的东西
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{test2();test3();return 0;
}

🏉2.1 qsort函数的模拟实现

那么这样的一个函数究竟是如何实现的呢?下面我们就来试着去模拟一下这样一个函数的实现。

在排序的底层逻辑上我们可以使用我们的冒泡排序去实现我们的我们的排序,由于我们这一次要排的数据不再是单一的整数型,所以我们的数据交换,还要再弄一个函数,或者在原本函数进行改动(但是在原本函数上改动代码会有点偏长,小赵在这里就再弄一个函数去搞定这件事)。

交换函数

void _swap(void* p1, void* p2, int size)
{int i = 0;for (i = 0; i < size; i++)//这里其实就是对我们数据的每一个字节进行交换{char tmp = *((char*)p1 + i);//char刚好占一个一个字节*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}

冒泡排序

void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//这里主要就是冒泡排序前面的知识。
{int i = 0;int j = 0;for (i = 0; i < count - 1; i++){for (j = 0; j < count - i - 1; j++){if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)//这里是用了我们的比较函数,如果前面比后面的大{_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}

 最后浓缩在一起

#include<stdio.h>
int int_cmp(const void* p1, const void* p2)//比较函数
{return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)//交换函数
{int i = 0;for (i = 0; i < size; i++){char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//排序
{int i = 0;int j = 0;for (i = 0; i < count - 1; i++){for (j = 0; j < count - i - 1; j++){if (cmp((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[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int i = 0;bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);//打印我们的数组}printf("\n");return 0;
}

这样一个函数就可以对我们任意数据进行比较。 

🎾3.sizeof与strlen对比

sizeof和strlen函数其实也是我前面一直在和大家重点聊的两个家伙,这两个家伙我们在一起聊的时间其实很多,给小赵的感悟也很多,那么我们下面就再来看看这两个家伙究竟是什么不同,其实说起来也简单。

首先是sizeof这个操作符,这个函数的作用就是就算你给的东西所占的内存大小。而strlen则是统计我们字符串的长度或者字符串数组的长度,到‘\0’为止。可以说这个家伙真正打起来其实是在数组,如果不是在数组中这两个家伙几乎也打不起来。

好了下面我们结合我们的代码来聊

#include <stdio.h>
int main()
{char arr1[3] = {'a', 'b', 'c'};char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr1));return 0;
}

(这里要提一下我们的sizeof 可不会管你什么\0不\0只要你存在,那你今天就必须被记录下来。)

我们看到在字符串的统计中两个人都是很正常的,唯有在数组中我们的strlen似乎出了问题,那究竟 是什么问题呢?其实也很简单就是我们之前说的,strlen这个函数比较轴它一定要找到‘\0’为止,那么我们的字符串其实是会自动补一个‘\0’在字符串后面的,那我们的数组呢?我们的数组什么都不会补,那么我们死轴死轴的strlen就会一直去找一直去找我们的‘\0’终于它在一片茫茫的内存的黑暗中找到了我们的'\0',并且返回了它的位置,其实strlen函数这样也有点轴的可爱,不是吗?)那这个问题怎么解决呢?其实就是我们只需要在我们的第一个数组后面补一个‘\0’就可以了

🎾4.结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵有什么地方说得不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,小赵及时改正,感谢大家支持!!!

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

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

相关文章

【JS逆向学习】36kr登陆逆向案例(webpack)

在开始讲解实际案例之前&#xff0c;大家先了解下webpack的相关知识 WebPack打包 webpack是一个基于模块化的打包&#xff08;构建&#xff09;工具, 它把一切都视作模块 webpack数组形式&#xff0c;通过下标取值 !function(e) {var t {};// 加载器 所有的模块都是从这个…

【RocketMQ每日一问】RocketMQ nameserver的作用是什么?

Name Server 在 Apache RocketMQ 集群中扮演着以下几个重要作用&#xff1a; 服务注册与发现&#xff1a; Name Server 负责管理和协调整个集群&#xff0c;维护集群中所有 Broker 的信息&#xff0c;包括 Broker 的 IP 地址、端口号、存储容量等。当 Producer 和 Consumer 需…

【消息队列】RocketMQ 生产和消费中的集群模式和广播模式

在消息队列系统中&#xff0c;生产者和消费者的模式通常包括集群模式和广播模式。这两种模式分别用于不同的场景&#xff0c;具有不同的特点和优势。 1. 集群模式&#xff08;Cluster Mode&#xff09;&#xff1a; 在集群模式下&#xff0c;多个相同角色的实例组成一个集群&…

太帅了 soeasy两行命令创建一个文件系统

看三遍 看三遍 看三遍 A file list program that supports multiple storage, powered by Gin and Solidjs. 翻译:一个支持多个存储的文件列表程序&#xff0c;由Gin和Solidjs提供支持。 1.安装 命令1:创建目录 mkdir -p /opt/alist 命令2:创建容器 docker run -d \ --res…

一天吃透Redis面试八股文

目录&#xff1a; Redis是什么&#xff1f;Redis优缺点&#xff1f;Redis为什么这么快&#xff1f;讲讲Redis的线程模型&#xff1f;Redis应用场景有哪些&#xff1f;Memcached和Redis的区别&#xff1f;为什么要用 Redis 而不用 map/guava 做缓存?Redis 数据类型有哪些&…

UE5 C++ 基础变量类型,关于框架的初级练习

一.创建自己的MyGameModed。并在其中设置好GamePlay框架。 1.创建MyGameState,MyGameState,MyHUD,MyPawn&#xff0c;MyPawn&#xff0c;MyPlayerController,MyPlayerState。 2.并在MyGameMode的头文件里面&#xff0c;把GmaeMode里的框架需要的框架类都包含进去。 3.写一个构…

用微服务快速开发框架实现流程化办公!

实现流程化办公&#xff0c;可以助力企业提升市场竞争力&#xff0c;从而实现数字化转型升级。微服务快速开发框架是提升办公协作效率的得力助手&#xff0c;流辰信息以市场为导向&#xff0c;坚持自主研发与创新&#xff0c;始终为行业的进步和发展贡献应有的力量。 1、流程化…

【EI会议征稿通知】2024年通信技术与软件工程国际学术会议 (CTSE 2024)

2024年通信技术与软件工程国际学术会议 (CTSE 2024) 2024 International Conference on Communication Technology and Software Engineering (CTSE 2024) 2024年通信技术与软件工程国际学术会议 (CTSE 2024)将于2024年03月15-17日在中国长沙举行。会议专注于通信技术与软件工…

LLaVA-Plus:多模态大模型的新突破

前言 随着AIGC技术的不断进步&#xff0c;各类多模态大模型&#xff08;MLM&#xff09;开始蓬勃发展。在这一领域中&#xff0c;LLaVA-Plus的推出无疑是一次重大突破。作为LLaVA团队的最新工作&#xff0c;LLaVA-Plus不仅继承了LLaVA的优秀特性&#xff0c;还在此基础上进行了…

ChatGPT提示词保姆级教程

现在越来越多提示词教程&#xff0c;本文列个清单&#xff0c;方便以后整理&#xff0c;不定期更新&#xff0c;欢迎关注留言&#xff01; 后续更新欢迎关注 提示词&#xff08;prompt&#xff09;出来后&#xff0c;被称为一个新的岗位诞生&#xff0c;面向提示词工程师。 …

dns正反解析配置

1.配置正向解析baidu.com 1、下载bind包 [rootlocalhost ~]# yum install bind -y 2、对配置文件修改 [rootlocalhost ~]# vim /etc/named.conf 3、对数据文件修改 [rootlocalhost ~]# vim /var/named/baidu 4、重启服务 [rootlocalhost ~]# systemctl restart named.service 5…

hash应用

目录 一、位图 1.1、引出位图 1.2、位图的概念 1.3、位图的应用 1.4、位图模拟实现 二、布隆过滤器 2.1、什么是布隆过滤器 2.2、布隆过滤器应用的场景 2.3、布隆过滤器的原理 2.4、布隆过滤器的查找 2.5、布隆过滤器的插入 2.6、布隆过滤器的删除 2.7、布隆过滤器…

行云部署前端架构解析-前言 | 京东云技术团队

一个简单的自我介绍 项目规模 截止目前上万次代码提交&#xff0c;总代码行数1超过21万行&#xff0c;其中人工维护的代码超过 13万行&#xff0c;近千个文件。 前端线上服务直接对接的后端服务&#xff0c;达十多个。 跟很多应用一样, 它有行云的入口, 也有独立的服务, 还…

Rust-泄漏

在C中&#xff0c;如果引用计数智能指针出现了循环引用&#xff0c;就会导致内存泄漏。而Rust中也一样存在引用计数智能指针Rc,那么Rust中是否可能制造出内存泄漏呢? 内存泄漏 首先&#xff0c;我们设计一个Node类型&#xff0c;它里面包含一个指针&#xff0c;可以指向其他…

CC工具箱使用指南:【计算面积】

一、简介 在Arcgis中&#xff0c;如果要计算面要素的面积&#xff0c;有几种方法。 1、gdb数据会自带一个shape_area字段&#xff0c;这就是面的平面面积&#xff0c;单位是平方米&#xff1a; 2、在双精度字段上右键单击&#xff0c;在弹出的菜单中点击【计算几何】&#xf…

【JavaEE进阶】 依赖注⼊DI详解

文章目录 &#x1f334;什么是依赖注入&#x1f384;依赖注入的三种方法&#x1f6a9;属性注⼊(Field Injection)&#x1f6a9;构造⽅法注⼊&#x1f6a9;Setter注⼊&#x1f6a9;三种注⼊的优缺点 &#x1f333;Autowired存在的问题&#x1f332;解决Autowired存在的问题&…

【算法练习】leetcode算法题合集之栈和队列篇

普通栈 LeetCode20 有效的括号 LeetCode20 有效的括号 定义一个辅助map&#xff0c;判断字符串的字符是否在]})中。一旦是右括号就要弹出元素&#xff0c;判断匹配。 class Solution {public boolean isValid(String s) {if (s.length() % 2 1) {return false;}Map<Chara…

[labelme]labelme如何将标注的json格式转成png的mask文件掩码文件

labelme工具不仅仅具有标注功能&#xff0c;而且可以将json文件转化为png的分割训练文件&#xff0c;如果您是一个类别则可以直接用labelme_json_to_dataset进行转换最后提取对应的掩码文件即可进行语义分割训练。如果您是>2个类别则不推荐使用labelme工具进行转换&#xff…

开发实践5_project

要求&#xff1a; &#xff08;对作业要求的"Student"稍作了变换&#xff0c;表单名称为“Index”。&#xff09;获得后台 Index 数据&#xff0c;作展示&#xff0c;要求使用分页器&#xff0c;包含上一页、下一页、当前页/总页。 结果&#xff1a; ① preparatio…

CSS 实现卡片以及鼠标移入特效

CSS 实现卡片以及鼠标移入特效 文章目录 CSS 实现卡片以及鼠标移入特效0、效果预览默认鼠标移入后 1、创建卡片组件2、添加样式3、完整代码 0、效果预览 默认 鼠标移入后 在本篇博客中&#xff0c;我们将探讨如何使用 CSS 来实现卡片组件&#xff0c;并添加鼠标移入特效&#…