C语言进阶第三课-----------指针的进阶----------后续版

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


指针进阶后续

  • **作者前言**
  • 复习
    • 字符指针
    • 数组指针
    • 指针数组
    • 数组传参和指针传参
    • 函数指针
  • 函数指针数组
  • 指向函数指针数组的指针(可以理解为数组的指针,只是类型变了)
  • 回调函数
  • 总结

复习

在上一篇博客中我简单的介绍了一些,这里我们来复习一下

字符指针

#include<stdio.h>
int main()
{char* pc = "abcdef";printf("%p\n", pc);printf("%c", *pc);return 0;
}

在这里插入图片描述
pc存放的是字符a的地址,不是字符串
前面我们还学习到了const char * pa 和char *coust pa

数组指针

简单的讲就是指向数组的指针

#include<stdio.h>
int main()
{int arr[10] = { 0 };int(*parr)[10] = &arr;printf("%p\n", parr);printf("%p", parr + 1);return 0;
}

在这里插入图片描述
可以计算出两个地址相差40,也就是40个字节
这里我们又会引出一个知识点,就是数组名的理解,只有&arr 和sizeof(arr)中的arr是整个数组,不是数组首元素地址,其他的数组名都是首元素地址
如果是二维数组传参的话,传递的是第一行的地址

指针数组

简单的说还是数组,只不过元素的类型是指针类型

#include<stdio.h>
int main()
{int a = 0;int b = 0;int c = 0;int* arr[] = { &a, &b,&c };printf("%p", arr[0]);return 0;
}

数组传参和指针传参

数组传参
1. 传递的是数组首元素的地址
2. 一维数组传参,传递的是第一个元素的地址
3. 二维数组传参,传递的是第一行的地址
4. 数组在传参的时候,形参可以写成数组或者指针
5. 如果是二维数组传参,形参为指针要使用数组指针

函数指针

顾名思义就是指向函数的指针

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int main()
{int a = 4;int b = 5;int (*pA)(int, int) = Add;printf("%d", pA(a, b));return 0;
}

在这里插入图片描述
在这里&Add和Add都是传递函数的地址过去,还有一点就是(*pA)(a, b)和(pA)(a, b)是一样的,

函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,可以理解是存放地址的数组。那函数指针数组就是存放函数指针的数组,类型就是函数指针类型,那我就以上面的为例

#include<stdio.h>
int Add(int a, int b)
{return a + b;
}
int main()
{int a = 4;int b = 5;int (*parr[])(int, int) = { Add };printf("%d", parr[0](a, b));return 0;
}

在这里插入图片描述
看看是一样的效果
那我们来写一个计算器

#include<stdio.h>
void menu()
{printf("*************************************\n");printf("*****     0.exit       1.add   *****\n");printf("*****     2.sub        3.mul    ******\n");printf("*****           4.div          ******\n");printf("*************************************\n");
}
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 catf(int(*pa)(int, int))
{printf("输入两个整数:>");int num1 = 0;int num2 = 0;scanf("%d %d", &num1, &num2);return pa(num1, num2);
}
int main()
{while (1){menu();int input = 0;printf("请选择:>");scanf("%d", &input);int (*arr[])(int, int) = { NULL,add, sub, mul, div };if (!input){printf("结束");break;}else if (input > 4){printf("输入错误\n");continue;}printf("%d\n", catf(arr[input]));}return 0;
}

这里我运用了函数指针数组(转移表),可以节约很多的代码量,简单的说就是很好用

指向函数指针数组的指针(可以理解为数组的指针,只是类型变了)

前面我们学过了数组指针,这里的意思也是一样的只是叫法不一样,那这个类型就是函数指针类型
,函数指针类型是这样的 int (* )(), 那指向函数指针数组的指针的写法就是 int (* (*parr)[num])(),感觉很别扭
少说废话,直接演示

#include<stdio.h>
int main()
{int a = 5;int b = 2;int* arr[] = { &a, &b };int* (*parr)[2] = &arr;printf("%d", *(*parr[0]));return 0;
}

这是整形指针数组,指向整形指针数组的指针
同理函数指针数组的指针如下

#include<stdio.h>
int add(int a, int b)
{return a + b;
}
int main()
{int (*arr[])(int, int) = { add };int (*(*parr)[1])(int, int) = &arr;printf("%d", (*parr[0])(5, 6));return 0;
}

这里我只使用了一个函数,小可爱可以看看思路,和数组指针的写法是一样,只是这个类型不一样而已

回调函数

这个知识点主要涉及到函数指针的,比较重要,我们来学习
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
我们可以简单理解为回调函数的地址作为参数传递给另一个函数,也就是说回调函数通过通过传递本身地址给另外一个函数
上面我们写的计算器的代码就是使用到了回调函数
在这里插入图片描述
这里我简单的画了一下,回调函数就是add()函数,直接调用就是通过函数名直接调用

如果这里还不明白那就举一个函数qsort(),这是一个排序函数,底层是快速排序

#include<stdio.h>
#include<stdlib.h>
void print_arr(int* p, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", *(p + i));}printf("\n");
}
int cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
int main()
{int arr[] = { 9,8,7,4,5,6,1,2,3 };int sz = (sizeof arr / sizeof(int));print_arr(arr, sz);qsort(arr, sz, sizeof(arr[0]), cmp);print_arr(arr, sz);return 0;
}

可能一些小可爱不懂这个函数,那我就来介绍一下这个函数

在这里插入图片描述
void* :我们可以理解为一个垃圾桶,可以接收任何指针类型的数据,比如可以接收 int* 、char* 等类型的数据
base:就是我们要从哪里开始排序的第一个元素地址(待排序数据的起始地址)
size_t: 在一些编译器中 是unsigned int,而在一些编译器中就不是,这里就是unsigned int
num:我们要排序的元素个数
width:我们排序的元素中一个元素的大小,单位是字节
compare:是一个函数地址
我们可以想想前面我们的冒泡排序算法

#include<stdio.h>
#include<stdlib.h>
int main()
{int arr[] = { 1,5,4,8,26,8,5,2,15,9 };int sz = sizeof arr / sizeof(arr[0]);int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (j = 0; j < sz - i - 1; j++){if (arr[j] > arr[j + 1]){arr[j] = arr[j] ^ arr[j + 1];arr[j + 1] = arr[j] ^ arr[j + 1];arr[j] = arr[j] ^ arr[j + 1];}}}return 0;
}

主要核心就是两两相邻的元素进行比较,我们要理清排几次,每次怎么比较,冒泡排序有一些缺点,就是有点死板,只能排序某一类型,而qsort()可以排序任何类型,主要是在于compare函数的写法;这个函数是用来比较两个元素的,这两个元素可能是整形、字符、结构体等类型,使用的方法也会不同,但最终要返回 >0 <0 ==0的结果,简单的说就是我们要比较啥类型的这个函数就要写成该类型的比较方法
废话少说,直接演示
int ( *compare )(const void *e1, const void *e2 )
这是qsort函数里 的参数,我来解释一下,
可能有一些小可爱不明白void*,那我来解释一下
平时我们写一个int*类型的指针,我们加1会跳过4个字节,char* 类型加1跳过一个字节,如果我们在写比较两个元素比较的方法,不指明类型就会不懂加1跳过几个字节,所以规定了void *类型 不能解引用,也不能加减操作

#include<stdio.h>
#include<stdlib.h>
struct test
{char arr[20];int age;};
int cmp2(const void* e1, const void* e2)
{return ((struct test*)e1)->age - ((struct test*)e2)->age;
}int main()
{struct test arr[] = { {"ksdads", 15},{"ksdajk", 18} ,{"ksdadgjhjs", 10} };qsort(arr, sizeof arr / sizeof arr[0], sizeof(arr[0]), cmp2);return 0;
}

这是结构体比较年龄,如果要比较名字的话

int cmp3(const void* e1, const void* e2)
{return strcmp(((struct test*)e1)->arr, ((struct test*)e2)->arr);
}

那我们可不可以写一个以冒泡排序为逻辑的qsort函数呢?
我们来试试看

#include<stdio.h>
#include <stdlib.h>
#include<string.h>
void print(int* arr, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}// 比较两个元素
int cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
//比较字符
int cmp1(const void* e1, const void* e2)
{return *(char*)e1 - *(char*)e2;
}
//两个元素进行交换
void exchange(char* e1, char* e2, size_t byt)
{int i = 0;for (i = 0; i < byt; i++){char a = *(e1 + i);*(e1 + i) = *(e2 + i);*(e2 + i) = a;}}
void my_qsort(void* base, size_t sz, size_t byt, int (*conpi)(const void*, const void*))
{int i = 0;for (i = 0; i < sz - 1; i++){int j = 0;for (j = 0; j < sz - 1 - i; j++){if (conpi((char*)base + j * byt, (char*)base + (j + 1) * byt) > 0){exchange((char*)base + j * byt, (char*)base + (j + 1) * byt, byt);}}}}
void test(void *arr, int sz, size_t by)
{print(arr, sz);my_qsort(arr, sz, by, cmp);print(arr,sz);
}
int main()
{int arr[] = { 8,3,1,5,2,3,6,9,7,4,8,9,2,4};char arr1[] = "dsfdfgfdfgfdf";// 整形//test(arr, sizeof arr/ sizeof(arr[0]), sizeof (arr[0]));// 字符串printf("%s\n", arr1);my_qsort(arr1, sizeof arr1 / sizeof(arr1[0]), sizeof(arr1[0]), cmp1);printf("%s\n", arr1);return 0;
}

在这里插入图片描述
可能有一些小可爱不明白这里,那我来讲讲,因为,我们要构造一个qsort函数,那我们就该要传入对应的base(起始地址)、unm(长度)、wigth(一个元素的字节长度) 和函数cmp(用于比较两个的函数),
我们不知道要传入啥数据类型,我就用void*进行接收,使用上面这个方法是为了让对于的存储的内存进行交换,转换成char*是为了更好的计算
在这里插入图片描述
这幅图是我以两个元素进行交换的情况一个元素的内存可以拆分成内存单元(一个字节),转换成char*,加1,我们访问一个字节,这样就可以解决类型不同,访问的字节也不同的问题了

在这里插入图片描述

总结

到这里我就介绍结束了,有不懂的小可爱可以私聊我

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

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

相关文章

docker总结

Docker实用篇 0.学习目标 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署…

ddtrace 系列篇之 dd-trace-java 项目编译

dd-trace-java 是 Datadog 开源的 java APM 框架&#xff0c;本文主要讲解如何编译 dd-trace-java 项目。 环境准备 JDK 编译环境(三个都要&#xff1a;jdk8\jdk11\jdk17) Gradle 8 Maven 3.9 (需要 15G 以上的存储空间存放依赖) Git >2 (低于会出现一想不到的异常&#xf…

大语言模型之十-Byte Pair Encoding

Tokenizer 诸如GPT-3/4以及LlaMA/LlaMA2大语言模型都采用了token的作为模型的输入输出&#xff0c;其输入是文本&#xff0c;然后将文本转为token&#xff08;正整数&#xff09;&#xff0c;然后从一串token&#xff08;对应于文本&#xff09;预测下一个token。 进入OpenAI官…

Learn Prompt-Prompt 高级技巧:MetaGPT

MetaGPT是一项引起广泛关注的研究成果&#xff0c;它引入了一个将人工工作流程与多智能体协作无缝集成的框架。通过将标准化操作&#xff08;SOP&#xff09; 程序编码为提示&#xff0c;MetaGPT确保解决问题时采用结构化方法&#xff0c;从而减少出错的可能性。 &#x1f389…

【深度学习】ONNX模型快速部署【入门】

【深度学习】ONNX模型快速部署【入门】 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】ONNX模型快速部署【入门】前言搭建打包环境打包可执行文件总结 前言 之前的内容已经尽可能简单、详细的介绍CPU【Pytorch2ONNX】和GPU【Pyto…

蓝桥杯打卡Day12

文章目录 接龙数列冶炼金属 一、接龙数列OJ链接 本题思路:本题是一道经典的dp问题&#xff0c;设第i个数的首位数字是first&#xff0c; 末位数字是last。因为第i个数只可能加到一个以first结尾的接龙数列中使得这个接龙数列长度加1并且结尾数字变成last.所以状态转移方程为d…

设计模式_解释器模式

解释器模式 案例 角色 1 解释器基类 &#xff08;BaseInterpreter&#xff09; 2 具体解释器1 2 3... (Interperter 1 2 3 ) 3 内容 (Context) 4 用户 (user) 流程 (上下文) ---- 传…

Redis 面试题——缓存穿透、缓存击穿和缓存雪崩

目录 1.缓存穿透2.缓存击穿3.缓存雪崩4.总结 参考文章&#xff1a; 缓存实战&#xff08;1&#xff09;缓存雪崩、缓存击穿和缓存穿透入门简介及解决方案 1.缓存穿透 &#xff08;1&#xff09;问题描述&#xff1a;缓存穿透是指在高并发场景下&#xff0c;大量的请求访问一个…

23种设计模式汇总详解

设计原则 中文名称英文名称含义解释单一职责原则Single Responsibility Principle(SRP)任何一个软件模块都应该只对某一类行为者负责一个类只干一件事&#xff0c;实现类要单一开闭原则Open-Close Principle(OCP)软件实体&#xff08;类、模块、函数等&#xff09;应该是可以扩…

ChatGpt介绍和国产ChatGpt对比

1.ChatGPT是美国OpenAI研发的聊天机器人程序&#xff0c;2022年11月30日发布。ChatGPT是人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过理解和学习人类的语言来进行对话。 2.ChatGPT是一种基于自然语言处理的聊天机器人程序。它使用深度学习技术&#xff0c;通过对…

【Linux】常用工具(下)

Linux常用工具 一、Linux 项目自动化构建工具 - make/Makefile1. 依赖关系和依赖方法2. 伪目标3. make/Makefile 具有依赖性的推导能力&#xff08;语法扩展&#xff09;4. 编写一个进度条代码&#xff08;1&#xff09;缓冲区&#xff08;2&#xff09;\n 和 \r&#xff08;3&…

软件测试-BUG

软件测试-BUG 1.如何合理创建一个BUG 创建bug的要素&#xff1a; 软件的版本发现问题的环境发现问题的步骤预期结果实际结果 Bug报告&#xff1a; 软件版本&#xff1a;Google Chrome浏览器&#xff08;具体版本号&#xff09; 发现问题环境&#xff1a;在Windows 10操作系统…

ASO优化之如何给应用选择竞争对手

在选择竞争对手过程中&#xff0c;最常见的错误之一是没有考虑到自己的应用与同一行业的其他应用相比的范围。例如如果我们刚刚发布了一个应用程序&#xff0c;那么最好的办法就是专注于研究和自己同一级别的应用。 1、研究主要关键词。 首先选择5到10个可以定义产品类型的主要…

什么是ELK

什么是ELK ELK 并不是一个技术框架的名称&#xff0c;它其实是一个三位一体的技术名词&#xff0c;ELK 的每个字母都来自一个技术组件&#xff0c;分别是 Elasticsearch&#xff08;简称 ES&#xff09;、Logstash 和 Kibana。 三个技术组件是独立的&#xff0c;后两个被elast…

【基于Thread多线程+随机数(Random)+java版本JDBC手动提交事务+EasyExcel读取excel文件,向数据库生成百万级别模拟数据】

基于Thread多线程随机数&#xff08;Random&#xff09;java版本JDBC手动提交事务EasyExcel读取excel文件&#xff0c;向数据库生成百万级别模拟数据 基于Thread多线程随机数&#xff08;Random&#xff09;java版本JDBC手动提交事务EasyExcel读取excel文件&#xff0c;向数据库…

《动手学深度学习》(pytorch版+mxnet版)2023最新

我又来推书了&#xff0c;这次分享的这本书可是重量级&#xff0c;目前已经被55个国家300所大学用于教学&#xff0c;同时受到了学术界与工业界的强烈推荐。 这本书就是李沐、阿斯顿张、立顿、斯莫拉四位大佬联合编写的《动手学深度学习》。本书面向中文读者&#xff0c;能运行…

基于SSM的旅游网站系统

基于SSM的旅游网站系统【附源码文档】、前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;管理员、用户 管理员&#xff1a;用户管理、景点…

避免分库分表,绿普惠的分布式数据库选型与实践

作者&#xff1a;翻墨&#xff0c;绿普惠科技&#xff08;北京&#xff09;有限公司架构师 一项数据显示&#xff0c;今天大气中的二氧化碳水平比过去 65 万年高了 27%。主要原因来自于工业化需求下的煤炭燃烧、汽车尾气。随着人类活动造成的温室效应加剧&#xff0c;环保越来越…

小程序开发一个多少钱啊

在今天的数字化时代&#xff0c;小程序已经成为一种非常流行的应用程序形式。由于它们的便捷性、易用性和多功能性&#xff0c;小程序吸引了越来越多的用户和企业。但是&#xff0c;很多人在考虑开发一个小程序时&#xff0c;都会遇到同一个问题&#xff1a;开发一个小程序需要…

JavaWeb基础学习(5)

JavaWeb基础学习 一、Filter1.1 Filter介绍1.2 Filter快速入门1.3、Filter执行流程1.4、Filter使用细节1.5、Filter-案例-登陆验证 二、Listener2.1 Listener介绍2.2、ServletContextListener使用 三、AJAX3.1 AJAX介绍与概念3.2 AJAX快速入门3.3 Axios异步架构3.4 JSON-概述和…