带你学C语言~指针(3)


目录

✍0.前言

🚀1.字符指针变量

🚅2.数组指针变量

🐱‍🏍2.1.数组指针变量是什么

🐱‍🏍2.2数组指针变量怎么初始化

🚢3.二维数组传参的本质

🚀4.函数指针变量

✈4.1函数指针变量创建

✈4.2函数指针变量的使用

🏹5.函数指针数组

🏆6.转移表

🌍结束语


✍0.前言

在上一节指针中我们讨论了一系列和指针相关的知识,包括二级指针,指针数组,但光靠这些知识还无法完全理解指针,因为指针还有函数指针这样一极其关键的类型,而我们上一章也没有具体聊如何存储一个字符串,以及如何访问它。好了下面就随着小赵的步伐一起来看看这些知识吧。

🚀1.字符指针变量

首先我们回顾一下我们的字符指针的定义

int main()
{char m = 'a';//定义一个字符变量char* ch = &m;//字符指针变量,接收m的地址*ch = 'w';//对ch解指针,对m里面的值进行修改return 0;
}

我们在上一章是讲过这种单个字符的指针的变量,但如果我们存入字符指针的是一个字符串而不是字符结果又会是怎么样的呢?

int main()
{const char* pstr = "abcdfe";printf("%s", pstr);return;
}

那这里的运行原理又是怎么样的呢?是不是我们就是把一个字符串放入了指针里呢?其实不是,因为指针也无法存储这种数据,它存储的毕竟是地址,那究竟是咋回事呢?其实这里我们的指针存放的是我们这个字符串的首地址, 可以看这样的一个代码

这样就证明了我们的我字符指针存储的其实是我们的字符串的首字母的地址。那么知道了这些知识我们就可以去看一道极其有意思的题目

int main()
{char str1[] = "hello world";char str2[] = "hello world";const char* str3 = "hello world";const char* str4 = "hello world";if (str1 == str2){printf("str1 and str2 are same\n");}else{printf("str1 and str2 are not same\n");}if (str3 == str4){printf("str3 and str4 are same\n");}else{printf("str3 and str4 are not same\n");}return 0;
}

大家可以先自己猜测一下这个代码运行的结果,然后和小赵这边的答案比对一下。

 从这个结果我们可以惊讶的发现我们的str3和str4居然是一样的,这又是为什么呢?

首先我们来说一下为什么str1和str2不一样,因为两个数组开辟的地址不一样,两个存了都在数组里存了一样的代码而已。

接着就是我们的str3和str4,首先是他们接收的地址来自字符串,而这个字符串其实是在我们的内存中开辟一块地区的,这块地区是这个字符串的,那么我们的地址去访问这个字符串的时候其实访问的地方是一样的,就是都用了一个字符串的首字母的地址。

上面这个题目来自我们的《剑指offer》这样一本书,各位如果有兴趣可以去看看哦。

🚅2.数组指针变量

在上一章小赵和大家聊了我们的指针数组,即在数组中存储指针,那么我们是否存在指针去存储我我们的数组呢?答案是有的那么我们来看看它长什么样子吧。

在这里我们可以对比一下我们的指针数组

在这里我们发现当我们的*与ptr结合时候,他就是个指针,那么其(*ptr)[10]也就是个数组指针,而另一个呢,我们发现*是与int结合那么它表示的就是我们数组里面存的是指针类类型也就是指针数组啦。

🚢3.二维数组传参的本质

在上一章,小赵与大家重点聊了,一维数组传参的本质,说一维数组传参其实传的是首元素的地址,那么我们二维数组根据这个推广其实大家也可以猜到其实传的就应该是二维数组首元素的地址,那么二维数组的首元素是什么呢?首先我要带大家回想的就是二维数组是由什么组成的,在前面小赵重点和各位聊过,二维数组其实就是由一个个的一维数组组合而成,那其里面的元素就是我们的一维数组那我们传入的首元素的地址其实不就是我们的二维数组里面首个一维数组的地址吗?那知道了这个,我们就可以试试去用我们上述的数组指针去代替我们原本的代码。

原本的代码

#include <stdio.h>void test(int a[3][5], int r, int c){int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}}int main(){int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

用指针后的

#include <stdio.h>
void test(int(*p)[5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", *(*(p + i) + j));}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

 其实区别并不大,小赵在这里只是解释了一里面的本质,方便大家更进一步的的理解之前我们的代码,在使用方面各位可以自由选择。

🚀4.函数指针变量

在开始我们的函数指针变量之前啊,我们要先看看我们的函数是否存在地址,然后就是它是否和数组一样是名字就是他们的地址呢?只有知道了这两点,我们才能更好地开展我们下面的函数指针。

void test()
{printf("hehe\n");
}
int main()
{printf("test:%p\n", test);printf("&test:%p\n", &test);
}

在这里我们看到我们两个地址是一样的那其实也就说明了1函数有地址,2函数名就是他们的地址

🐱‍🏍4.1函数指针变量创建

其实函数指针的创建我们可以参考一下数组指针,我们发现在数组指针的创建过程中大体并没有大的变化只是变化了数组的名字,改为了指针的名字,那么我们的函数指针创建也是如此。

void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;
int Add(int x, int y)
{return x + y;
}int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

 

🐱‍🏍4.2函数指针变量的使用

#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}

🐱‍🏍4.3两段有趣的代码

好了学习了上面的一系列知识后,我们下面给各位上盘大菜,就是我们的这样两段代码,也是小赵发在我们的博客上求助的两段代码。

(*(void (*)())0)();

先看第一个如何理解这样一段代码呢

小赵在这里为大家画个图,这个图是由里面向外看的。对这个代码做个较为清晰的解释。

第二段代码是

void (*signal(int , void(*)(int)))(int);

在这里小赵也是给大家画一个图方便大家理解

这两段代码出自我们的《C陷阱与缺陷》这本书,有兴趣的小伙伴可以看看,这本书可以提升大家对于C语言的理解和认知,让大家更好地学习和使用我们的C语言。 

 🎇补充typedef关键字

看了上面两段代码,我们有时候可能会感觉我们这个返回类型好大一块,还要分开写,这样好像并不方便我们的C语言读者的阅读和使用那有木有办法让我们的代码缩短呢,让返回类型只在函数名前面也好啊,答案是有的,这里就要引入我们的typedef这个关键字,这个关键字是干嘛的呢?我们可以先上网搜搜看,小赵这里用的网站是C 库函数 – srand() | 菜鸟教程

 

我们可以看到它的主要作用其实就是改名,把我们原本的类型改掉 名字有了这个我们就可以优化前面的代码了。

 

当然还可以用来修改其他的如int 

注意一下修改名字的位置就可以了。

🏹5.函数指针数组

上一章我们讨论了指针数组,那这一章我们学了函数指针是否可以定义一个函数指针数组呢?,来看下面。

int (*parr[3])();//parr 先和 [] 结合,说明 parr1是数组,而数组的元素就是外面这一圈int (*)() 类型的函数指针

接着我们可以试着创建自己的函数指针数组 

int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int main()
{int(*p[2])(int x, int y) = { add, sub };
}

这里要注意的是我们函数指针数组定义好的元素类型是不能改的,不能有的是传入一个元素有的是传入两个,也不能一个有返回值一个没有,或者返回类型不行。 

🏆6.转移表

好了,小赵下面带大家做一个转移表,,同时使用一下我们的函数指针数组

#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;
}

在这个转移表中我们其实就是奖我们的各个功能能够灵活调用有点像我们的计算机,那我们能否用我们的函数指针数组对其进行改造呢?答案是可以的。

#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;int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表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);if ((input <= 4 && input >= 1)){printf("输⼊操作数:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret = %d\n", ret);}else if (input == 0){printf("退出计算器\n");}else{printf("输⼊有误\n");}} while (input);return 0;}

我们会发现我们是用我们的函数指针数组让我们的代码更加美观优化,更加好阅读。 

🌍结束语

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

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

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

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

相关文章

Python3.12 新版本之f-string的几个新特性

目录 概述 f-string表达式 1. 支持转义字符 2. 支持多行字符串 3. 支持重复使用的引号种类 4. 支持无限嵌套 附录 Python3.12新语法特性 概述 Python 3.12在10月2日发布&#xff0c;新版本包含一系列对语言和标准库的改变。 库的改变主要集中在清理已弃用的 API、可…

windows+ubuntu离线安装翻译软件有道词典

背景&#xff1a; 某些情况下&#xff0c;需要在无法连接互联网的电脑上翻译单词&#xff0c;句子以及段落&#xff0c;就需要能离线安装和翻译的翻译软件&#xff0c;具备一定的词库量&#xff0c;目前找到了《有道词典》。 windows 亲测&#xff0c;无法联网的win10中安装…

OpenAI GPT 模型 API 接口新增参数 top_logprobs 和 logprobs

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 在最新的 OpenAI 官方 APIs 文档中&#xff0c;Create chat completion 中新增了 top_logprobs 和 logprobs 这两个关键参数。 官方文档地址&#xff…

黑马程序员SSM框架-Spring

视频链接&#xff1a;Spring-00-Spring课程介绍_哔哩哔哩_bilibili Spring Framework系统架构以及学习顺序 核心概念&#xff08;IoC、DI、Bean&#xff09; IoC入门案例 导入坐标 提供需要被管理的类&#xff08;Dao&#xff09;和需要被注入的类&#xff08;Service&#x…

netcore 上传文件到阿里云OSS问题

AliYunOSSHelper类&#xff1a; namespace VOL.WebApi {public class AliYunOSSHelper: IScopedDependency{private ILogger _logger;/// <summary>/// 阿里云帮助类/// </summary>/// <param name"logger"></param>/// <param name&quo…

windows安全配置实验手册

访问控制策略&#xff08;L1940520022J&#xff09; 预备知识 Windows 7中&#xff0c;不仅有面向软件的限制方法&#xff0c;还增加了一种名为AppLocker的访问控制策略&#xff08;仅适用于企业版和旗舰版&#xff09;。 实验环境 操作系统类型&#xff1a;windows 7。 实…

二叉树OJ题——4.相同的树

100. 相同的树 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 首先比较根节点是否相同&#xff0c;然后分别比较左右子树是否相同。 */bool isSameTree(struct TreeNode* p, struct TreeNode* q) {if(pNULL&&qNULL){return true;}if(pNULL||qNULL)…

利用html2Canvas将表格下载为html

给到我的需求是点击按钮时请求后端接口&#xff0c;根据后端返回的数据&#xff0c;生成表格,并将表格的内容直接下载为html,如下图。 平常做的下载都是后端返回二进制流&#xff0c;这次前端做下载那就必须把页面先画出来&#xff0c;因为下载下来的表格在页面上是不显示的&a…

直接插入排序和希尔排序

文章目录 前言概述直接插入排序本质分析代码总结 希尔排序一组一组排序多组同时进行完整的代码 总结 前言 排序&#xff1a; 所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a; …

浅谈Java反射中的getFields()方法和getDeclaredFields ()方法

目录 1. 概念2. getFields()方法2. getDeclaredFields()方法4. 总结 1. 概念 反射是Java中一种强大的机制&#xff0c;允许在运行时获取、检查和操作类、方法、字段等信息&#xff0c;而不需要在编译时知道这些信息。 其中字段&#xff08;Field&#xff09;在Java中是类中用…

Unity向Web服务器上传和下载图片

Unity向Web服务器上传和下载图片 如果本片有看不懂的请查看我上篇文章&#xff1a;[Unity与Web服务器Post&#xff0c;Get](https://blog.csdn.net/qq_42194657/article/details/103031573)一、上传和下载图片1.在Unity中创建一个RawImage并在WebManager.cs脚本中添加一个Textu…

模式识别与机器学习-特征选择和提取

模式识别与机器学习-特征选择和提取 特征选择一些距离测度公式独立特征的选择准则一般特征的散布矩阵准则 离散K-L变换 谨以此博客作为复习期间的记录。 常见分类问题的流程&#xff0c;数据预处理和特征选择提取时机器学习环节中最重要的两个流程。这两个环节直接决定了最终性…

mac电脑系统性能检测:Geekbench 6最新 for mac

Geekbench 6是一款跨平台的性能测试软件&#xff0c;旨在全面衡量电脑在不同任务和应用程序中的性能。它适用于Windows、macOS、Linux等操作系统&#xff0c;以及移动设备如Android和iOS。 Geekbench 6主要特点包括&#xff1a; 全面覆盖各种性能测试领域&#xff0c;包括CPU…

CTF-PWN-栈溢出-中级ROP-【BROP-1】

文章目录 BROP(Blind ROP)攻击条件攻击路线三种gadget函数参数构造利用PLT构造rdx的参数的gadget找输出函数的plt输出二进制文件内容利用plt表中存在跳转到got表中相应的地址 BROP(Blind ROP) 即没有得到源码或者可执行程序文件的情况的文件下&#xff0c;只有一个提供的功能端…

C++学习实践(一)高频面试问题总结(附详细答案)

文章目录 一、基础常见面试题1、数组和链表区别2、深拷贝和浅拷贝相关问题的区别3、a和a区别4、c内存模型5、四种强制转换和应用场景 二、指针相关1、指针和引用的区别2、函数指针和指针函数3、传指针、引用和值4、常量指针和指针常量5、野指针6、智能指针的用法 三、关键字作用…

mySQL事务与存储引擎

目录 mySQL事务 1.事务的概念 2.事务的ACID特点 3.多客户端同时访问一个表时&#xff0c;出现的一致性问题 4.事务的隔离级别 5.事务的隔离级别作用范围 查询全局事务隔离级别 设置全局事务隔离级别 ​编辑查询会话事务隔离级别 设置会话事务隔离级别 6.事务控制语句…

这一次,我准备了 20节 PyTorch 中文课程

对于刚接触深度学习的小白来说&#xff0c;PyTorch 是必会的框架。 只是&#xff0c;很多小伙伴还没来得及开启学习之路&#xff0c;一个最重要的问题就摆在了面前&#xff1a; PyTorch&#xff0c;该怎么学呢&#xff1f; 很多同学会自己在网上找资料&#xff0c;不仅耗费时间…

如何使用IIS代理iServeriPortal

刘大 这里写目录标题 前言1.IIS使用http协议&#xff0c;iServer&iPortal使用http协议具体操作1.1 开启IIS管理器&#xff0c;并安装APR组件和url重写组件1.3 启用代理1.4 添加URL重写规则1.4.1 设置保留原始主机标头1.4.2 修改注册表1.5 iPortal的额外配置1.5.1 增加新的入…

使用防火墙是否可以应对DDoS攻击?

很多游戏行业公司对网络安全不够了解&#xff0c;觉得装个防火墙就可以万事大吉了。实际上使用防火墙确实是解决DDoS攻击问题的一种有效方法&#xff0c;一些更先进的防火墙还可以采用其他防御措施&#xff0c;例如:深度包检测、行为分析、人工智能等&#xff0c;来识别和防御各…

Vi文本编辑器如何使用,这里有详细步骤

Vi是一个强大的文本编辑器&#xff0c;包含在大多数Linux系统中&#xff0c;甚至是嵌入式系统中。有时&#xff0c;你必须在不包括更友好的文本编辑器的系统上编辑文本文件&#xff0c;因此了解Vi是至关重要的。 与Nano不同&#xff0c;Nano是一款易于使用的终端文本编辑器&am…