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

目录

🏉前言

🚀 数组名的理解

🚀使用指针访问数组

✈一维数组传参的本质

✈冒泡排序

🏆二级指针

🏆指针数组

🏆指针数组模拟二维数组

🎉结束语


🏉前言

上一章,小赵和各位聊了关于C语言指针的开篇章,我们聊了指针最初的模样,但指针这个东西远远不止上一章那样简单,因为我们开辟的内存远远不止这么简单,毕竟还有数组的开辟,还有函数等等这些,包括我们的指针它也是有内存的,那我们究竟该如何去访问它们的地址,如何使用它们的地址内存,这样的地址内存又究竟对我们编写代码会带来什么样革命性的改变,下面小赵讲讲一一为你解答。

🚀 数组名的理解

首先想和各位说的一点就是,其实我们的数组名就是我们这个数组的地址,那这个数组的地址究竟是整个数组的地址呢?还是说是我们的数组首元素的地址,还是说是我们数组结尾的那个地址。这里我们打印出来看看。

 那我们通过我们对我们的数组打印地址,我们发现我们的首元素地址和我们的数组名代表的地址是一样的,那么我们的数组的地址其实也就明白了。

那如果我们对我们的地址名取地址呢?这里我们也可以打印看看。

这里我们惊讶的发现这两个地址,居然是一样的,那这样是否可以说明我们&arr与arr,是一样的?

这里小赵给大家的解释一下,虽然这两个地址是一样的但含义却是极大的不同,这个就和我们之前聊的单位一样,(char int 等),&arr取得是整个数组的地址,所以如果你对它加一,它的单位实际上就是加了一个数组,那另外一个呢?如果只是arr,它其实代表的就是首元素地址,几乎等价于&arr[0],那它加的就是以每一个数组的元素的大小为单位,那么它的加一,就是加到数组的下一个数组的元素。

🚀使用指针访问数组

好了有了上面的知识作为支撑,我们就可以试着去探讨如何用我们的指针和我们的数组相联系了。

int main()
{int arr[10] = { 0};int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for (int i = 0; i < sz; i++){scanf("%d", p + i);//给数组每个位置赋值}for (i = 0; i < sz; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

这个代码小赵要和大家解释一下,这里我们的数组是定义好的,而且我们的指针指向的是首元素的地址,那么我们其实对它加加就相当于从数组首元素向后走,其实还要给大家补个概念,其实我们的指针指向那个地址,那我们的p其实就相当于这个地址,这也就是我们说的指针=地址。那这里就是地址就不用加&,而下面我们打印的是地址里面的东西所以要解地址操作。 

那这时候有人要问了,能不能超过这个数组进行访问了,其实是不行的,因为这里就是一种越界。

int main()
{int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for (i = 0; i < sz+1; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

那这个空间就是未知打印的东西也是未知的。再看这个 

int main()
{int arr[3] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);//数组有多少个元素int* p = arr;//取首元素地址for ( i = 0; i < sz+1; i++){scanf("%d", p + i);//给数组每个位置赋值}for (i = 0; i < sz+1; i++){printf("%d", *(p + i));//打印数组每个位置}return 0;
}

在这里我们就使用了未被定义的空间,那么我们的程序是会报错的。所以这种未被定义的空间是不能使用的,我们指针指向它的时候,其实我们的指针也就相当于野指针。 

✈一维数组传参的本质

#include <stdio.h>
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;

在讲一维数组传参本质前,先给大家看一段代码,这个代码应该就是我们学指针前所用的数组传入函数的方式,但我们其实不知道的是,我们传入的究竟是整个数组的,还是数组的某一个呢?这个代码刚好可以帮我门验证一下这一点。 

在这里我们可以看到我们代码运行后的结果是 10,1这就证明了,我们传入的数组其实只传了一个,那么为什么会这样呢?其实结合小赵这章所聊的内容,我们可以知道我们传入函数的是数组名,那数组名是什么呢?其实就是指针啦,那就证明我们传入函数的其实是个数组首元素指针。那这就好办了,那我们以后数组,不就可以用指针传了吗?而且指针改的是地址,可以让我们在函数中的操作,进入我们的数组中。这里补的一点是,sizeof(arr),计算的是这个所代表的存在里面东西的大小,如果在函数外面,它表示就是整个数组(这一点比较特殊,只有在sizeof这里才代表这个意思。),而我们在函数里面它代表的就是我们传入的首元素的地址,计算的也只有数组首元素的大小。)

void test(int* arr)//参数写成指针形式
{printf("%d\n", sizeof(arr) / sizeof(arr[0]));//计算⼀个指针变量的⼤⼩
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}

这里我们用指针试试 ,发现结果一样,与我们的猜想是一致的。

✈冒泡排序

有了上面的知识我们就可以试着用指针来编我们的冒泡排序了。

#include <stdio.h>
void BOBBLE(int* a)//优化版冒泡排序
{for (int m = 0; m < 4; m++){int flag = 1;//判断是否排序完成for (int j = 0; j < 4 - m; j++){if (a[j] >a[j + 1])//如果a[j+i]都大于arr[j],则证明排序已经完成了{flag = 0;//如果排序未完成,那么将falg置为零int t = a[j + 1];a[j + 1] = a[j];a[j] = t;}}if (flag == 1)//如果完成,结束循环break;}}
int main()
{int a[5] = { 0 };int i;for (i = 0; i < 5; i++){scanf("%d", &a[i]);}BOBBLE(a);for (int m = 0; m < 5; m++){printf("%d", a[m]);}return 0;
}

我们会发现有了指针的加入,让我们原本冗杂的长的代码变得更容易看懂,可以将我们的冒泡排序加入到我们的函数中去了,而且由于我们的指针改的其实是数组地址里面的内容,那这就让我们的数组可以真正的改变了(这个有点类似(1)的交换 )。

同时我们对我们的之前的冒泡排序进行了优化,让我们的代码可以不用执行的很复杂,大大加快代码了运行速度。

🏆二级指针

那好了数组解决了吧,那我们一级指针的地址咋说,谁能来管管呢?就这样我们的二级指针诞生了。

当然,我们得先看看我们指针是否有地址,看看它的地址。

我们看到我们指针指向的地址,与我们指针本身的地址是不一样的,这也就间接证明了,我们的指针其实是有自己独立的地址。

那我们的二级指针是什么样的,其实也蛮简单的,我们可以用上一章的思想解决,打开一个空间要一个钥匙,那我们现在打开两个呢?那不就是两个吗。

int main()
{int a = 0;int* p = &a;int** b = &p;printf("%p\n", &a);printf("%p\n", p);printf("%p\n", &p);printf("%p", b);
}

那我怎么才能访问到里面的a能其实也就是开两次锁。

🏆指针数组

那紧接着下来就是,我们有木有这样一个数组里面存的是指针呢?那其实也是有的。我们之前聊到,数组前面的那个就是它里面的元素类型,那我们只要将前面写成int*,char*等就可以了。

int main()
{int a = 0;int* p = &a;int* b[1] = { p };//指针数组printf("%d", *(b[0]));
}

🏆指针数组模拟二维数组

那一维数组聊完了,那二维数组呢?这里就不得不提小赵前面的概念了,二维数组里面每个元素其实就是一维数组,那既然是一维数组,我们是不是可以试着用一维数组的首元素地址代替呢?然后再对这个元素地址进行解指针操作,加一加二等,这样是不是就让指针数组模拟二维数组的操作完成了。 

#include <stdio.h>
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* parr[3] = { arr1, arr2, arr3 };//数组名是数组⾸元素的地址,类型是int*的,就可以存放在parr数组中int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);//这里要解释一下,其实我们a[1]=*(a+1)的操作,那我们这里的arr[1][2]其实也就等于*(arr[1]+2)的操作。}printf("\n");}return 0;
}

这里要和家人们 解释的其实就是我批注的那一块,我们的arr[]这个操作其实就是在对我们的arr进行解指针的操作,而括号里面的数就是我们arr+数字,地址的移动,然后进行解指针。 

🎉结束语

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

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

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

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

相关文章

关于“Python”的核心知识点整理大全28

目录 11.1.5 添加新测试 11.2 测试类 11.2.1 各种断言方法 unittestModule中的断言方法&#xff1a; ​编辑11.2.2 一个要测试的类 survey.py language_survey.py 11.2.3 测试 AnonymousSurvey 类 test_survey.py 往期快速传送门&#x1f446;&#xff08;在文章最后&…

计算机操作系统-第十九天

目录 调度器/调度程序 闲逛进程 调度器/调度程序 ②、③由调度程序引起&#xff0c;调度程序决定了&#xff1a;让谁运行&#xff08;调度算法&#xff09;运行多长时间&#xff08;时间片大小&#xff09; 调度时机&#xff08;什么事件会触发”调度程序“&#xff09;&…

proxysql读写分离组件部署

一、前言 在mysql一主两从架构的前提下&#xff0c;引入读写分离组件&#xff0c;可以极大的提高mysql性能&#xff0c;proxysql可以在高可用mysql架构发生主从故障时&#xff0c;进行自动的主从读写节点切换&#xff0c;即当mysql其他从节点当选新的主节点时&#xff0c;proxy…

HuatuoGPT

文章目录 HuatuoGPT 模型介绍LLM4Med&#xff08;医疗大模型&#xff09;的作用ChatGPT 存在的问题HuatuoGPT的特点ChatGPT 与真实医生的区别解决方案用于SFT阶段的混合数据基于AI反馈的RL 评估单轮问答多轮问答人工评估 HuatuoGPT 模型介绍 HuatuoGPT&#xff08;华佗GPT&…

Elasticsearch 向量相似搜索

Elasticsearch 向量相似搜索的原理涉及使用密集向量(dense vector)来表示文档,并通过余弦相似性度量来计算文档之间的相似性。以下是 Elasticsearch 向量相似搜索的基本原理: 向量表示文档: 文档的文本内容经过嵌入模型(如BERT、Word2Vec等)处理,得到一个密集向量(den…

Semaphore 详解

1、Semaphore 是什么 Semaphore 通常我们叫它信号量&#xff0c; 可以用来控制同时访问特定资源的线程数量&#xff0c;通过协调各个线程&#xff0c;以保证合理的使用资源。 可以把它简单的理解成我们停车场入口立着的那个显示屏&#xff0c;每有一辆车进入停车场显示屏就会…

JDK各个版本特性讲解-JDK13特性

JDK各个版本特性讲解-JDK13特性 一、JAVA13概述二、语法层面特性1.switch表达式(预览)2.文本块(预览)2.1 概念2.2 问题2.3 目标2.4 语法细节1 基本使用2.5 语法细节2 编译器在编译时,会删除多余的空格2.6 语法细节3 转义字符2.7 语法细节4 文本块连接 三、API层次特性1.重新实现…

13、Kafka副本机制详解

Kafka 副本机制详解 1、副本定义2、副本角色3、In-sync Replicas&#xff08;ISR&#xff09;4、Unclean 领导者选举&#xff08;Unclean Leader Election&#xff09; 所谓的副本机制&#xff08;Replication&#xff09;&#xff0c;也可以称之为备份机制&#xff0c;通常是指…

为什么我的对话框创建失败了?菜鸟错误1

对话框中的资源要么被定义为一个整数&#xff0c;要么被定义为一个字符串。 仅仅一个简单的错误将会将其中的一个类型错误的变成另一个类型。我们来看一个例子。 >> 请移步至 www.topomel.com 以查看图片 << 你是否能发现其中的两处 “菜鸟级错误” ? 如果先获…

Elasticsearch:生成 AI 中的微调与 RAG

在自然语言处理 (NLP) 领域&#xff0c;出现了两种卓越的技术&#xff0c;每种技术都有其独特的功能&#xff1a;微调大型语言模型 (LLM) 和 RAG&#xff08;检索增强生成&#xff09;。 这些方法极大地影响了我们利用语言模型的方式&#xff0c;使它们更加通用和有效。 在本文…

Linux系统管理、服务器设置、安全、云数据中心

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 我们来快速了解liunx命令 文章目录 前言解析命令提示符linux的文件和目录文件和目录管理文件操作 进程管理命令系统管理网络管理 书籍推荐 本文以服务器最常用的CentOS为例 解析命令提示…

2024年完整湖北等保测评机构名单看这里!

等保测评机构是指经公安部认证的具有资质的测评机构&#xff0c;主要从事等级测评活动。一般过等保需要找正规具有资质的等保测评机构。那你知道2024年湖北等保测评机构有哪些&#xff1f;名单有吗&#xff1f; 2024年完整湖北等保测评机构名单看这里&#xff01; 1、湖北星…

接口测试【断言设置思路】实操

1 断言设置思路 这里总结了我在项目中常用的5种断言方式&#xff0c;基本可能满足90%以上的断言场景&#xff0c;具体参见如下脑图&#xff1a; 在这里插入图片描述 下面分别解释一下图中的五种思路&#xff1a; 1&#xff09; 响应码 对于http类接口&#xff0c;有时开发人…

无损编码——Slepian-Wolf理论

在信息论中&#xff0c;无损编码是一种重要的编码技术&#xff0c;其目的是通过尽量少的比特数来表示一段信息&#xff0c;同时保证信息的完整性和准确性。传统的无损编码方法往往只考虑单个源的编码问题&#xff0c;比如哈夫曼编码和算术编码等。然而&#xff0c;在实际应用中…

RTK、PPP与RTK-PPP?一文带您认识高精定位及如何进行高精定位GNSS测试!(一)

来源&#xff1a;德思特测试测量 德思特干货丨RTK、PPP与RTK-PPP&#xff1f;一文带您认识高精定位及如何进行高精定位GNSS测试&#xff01;&#xff08;一&#xff09; 原文链接&#xff1a;https://mp.weixin.qq.com/s/6Jb3DuJEhRGqFPrH3CX8xQ 欢迎关注虹科&#xff0c;为您…

#HarmonyOS:项目结构图

.hvigor&#xff1a;存储构建配置文件信息 .idea&#xff1a;存储项目的配置信息 AppScope&#xff1a;全局的共有资源存放目录

大模型评估中Pass@k值是如何计算的

在前面的博客中分别介绍了大模型评估过程不同指标的含义&#xff0c;以及如何通过代码&#xff0c;实现指标的收集。如果对如何运行代码生成结果和收集passk指标不清楚&#xff0c;可以参考这两篇博客。 如何对大模型进行评估上 如何对大模型进行评估下 Passk的来源 代码的生…

day34算法训练|贪心算法

1005.K次取反后最大化的数组和 两次贪心算法思路 1. 数组中有负数时&#xff0c;把绝对值最大的负数取反 2. 数组全为非负数时&#xff0c;一直取反最小的那个数 步骤&#xff1a; 第一步&#xff1a;将数组按照绝对值大小从大到小排序&#xff0c;注意要按照绝对值的大小…

基于YOLOv8深度学习的智能小麦害虫检测识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

JavaSE 排序

目录 1 概念1.1 排序1.2 稳定性 2 常见基于比较排序算法总览3 插入排序3.1 直接插入排序3.1.1 思想3.1.2 实现3.1.3 性能分析 3.2 折半插入排序3.2.1 思想3.2.2 实现3.2.3 性能分析 3.3 希尔排序3.3.1 思想3.3.2 实现3.3.3 性能分析 4 选择排序4.1 选择排序4.1.1 思想4.1.2 实现…