深入理解并打败C语言难关之一————指针(3)

前言:

  昨天把指针最为基础的内容讲完了,并且详细说明了传值调用和传址调用的区别(这次我也是做到了每日一更,感觉有好多想写的但是没有写完),下面不多废话,下面进入本文想要说的内容

目录:

1.数组名的理解

1.1数组名代表着什么

1.2.数组名的几种特殊情况

2.使用指针访问数组

2.1指针如何访问数组

3.一维数组传参的本质

4.冒泡排序算法的实现(先预热一下,这个太重要我写在下一篇了)

5.二级指针相关知识点

5.1二级指针是什么

5.2二级指针中的解引用操作

6.指针数组相关知识点

6.1指针数组

6.2利用指针数组模拟二维数组

正文:

1.数组名的理解

1,1数组名是什么

  其实数组名我在之前就解释过了,现在我们正式的来介绍它,数组名是数组首元素的地址,通过代码的撰写就可以看出来二者是一样的,代码以及展示图如下:

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

  在说上面之前,我想起来我忘记说%p这个占位符的作用了,这个占位符是代表着地址的占位符,想要打印地址就需要它,不难看出,上面两个地址打印出来的结果是一样的,所以可以更好的理解数组名就是数组首元素的地址,不过,这也不是绝对的,下面一种特殊情况数组名就不是数组第一个元素的地址。

1.2数组名的几种特殊情况

  1.2.1sizeof(arr)

  我们知道,sizeof运算符是用来计算长度的,上面讲了,arr指的是数组第一个元素的地址,按道理来讲,我们计算arr的长度应该就是第一个元素的长度即四个字节,但是看见下面的代码和打印结果:

#include<stdio.h>
int main()
{int arr[10] = {0};printf("%zd",sizeof(arr));return 0;
}

  我们不难看出,这里打印的结果是40个字节,为什么会打印出40呢?因为这是arr整体的元素个数,所以此时sizeof(arr)并不是第一个元素的长度,而是整个元素的长度,这个一定要记住,在某些企业的笔试题中或者正常的考试题中可能会出一个类似这样的题来迷惑你,所以我们要记住这个特殊情况! 不要出错,不要出错,不要出错!

  1.2.2&arr

  上面小编说过,arr是数组名,代表着数组第一个元素的地址,那么我们如果直接对arr去地址呢?这种情况我们先通过代码打印看一下:

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

  看到这里可能有些读者朋友会想,这三个的地址不都是相同的吗?是不是&arr就是数组第一个元素的地址呢?当然,如果真一样的话,我写这个小标题就一点意义都没有了,先不要着急,我们再看一下下面这个代码就可以知道&arr到底指的是什么了:

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

  这个时候就可以很清楚的发现三者的不同了,前两个肯定是一样的(前面的知识点的运用),而第三个,经过我们的计算,发现比前两个多了36个字节的内存空间 ,这是为什么呢?我也不卖关子了,其实取地址arr取到的是整个数组的地址,所以我们在进行加1操作的时候,其余都是加了一个字节,而&arr是跳跃了一个完全的数组,从而我们在想要取到地址的时候,一定要知晓取到的应该是一个完整的数组地址,不过刚开始指向的是数组第一个元素的地址罢了,在进行加减的时候一定要小心。

小结:

  我们在使用数组名的时候一定要注意这两个特殊情况,除了这两个特殊情况意外,其余的数组名都是代表首元素的地址

2.使用指针去访问数组

2.1指针如何访问数组

  其实用指针数组我在上一节课的代码中就已经实现了,以后我还是得改掉超前讲的坏毛病,不多废话,我们知道数组名就是数组第一个元素的地址,所以我们在使用输入函数的时候便可以用数组名来替代&arr[i]类似这样的写法,这样可以减少我们在使用scanf函数的时候忘记运用取地址符的尴尬情况(小编就时常忘记写这个,我还抱怨是编译器的问题,😅),在打印的时候我们就可以利用解引用操作符来实现操作,光说不练等于白说,下面来进行一个久违的环节,小小的例题

例1.利用指针访问数组随机打印五个数


#include<stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){scanf("%d", arr + i);}for (i = 0; i < 10; i++){printf("%d ", *(arr + i));  //在这里arr[i] == *(arr + i),甚至还可以写成[i]arr,我们在这里可以看出[]仅仅就是一个运算符罢了,不过为了视觉效果所以才这么用的}return 0;
}

  可以看出这个功效和我们正常使用一位数组是一样的,不过正如我前面所说的,这样写可以避免一些错误的出现,这也让我们之后写数组的方式变得更多了 ,一定要合理运用(scanf函数会时时常忘记写的读者朋友们可以这么写呦~)。

3.一维数组传参的本质

  数组我们是知道的,函数我们也是知道的,但是我们之前在利用函数传数组的参数的时候,我们也是传参的数组名,当时我们并没有考虑到数组名到底是干什么的,也不知道数组名就是数组第一个元素的地址,现在根据我们前面讲过的知识,下面小编出一个小小的题目,我们可以不可以通过函数来计算数组元素的个数呢?读者朋友们可以先思考一下,下面是相关代码:

#include<stdio.h>
void ceshi(int arr[10])
{printf("%zd", sizeof(arr));
}
int main()
{int arr[10] = { 0 };ceshi(arr);return 0;
}

   可以看出打印出来的结果是8(我这是在X64环境下写的),不难看出,此时打印的数组元素的地址的大小,并没有打印出整个元素的字节长度,原因很简单,我们知道arr是数组名,代表的是第一个元素的地址,所以我们传过去的是地址,计算长度的时候自然就是计算地址的长度了!所以我们每次想要传数组的时候,需要把数组的个数也传递过去。对于数组传参,其实是有两种形式的,下面来展示一下:

  

#include<stdio.h>
void ceshi1(int arr[10])
{//我就不写内容了,看外观别看内容
}
void ceshi2(int* arr)
{//这两个是一样的,小编比较喜欢写第一个的形式(说实在的这个形式在我复习的时候我才记着有)
}
int main()
{int arr[10] = { 0 };ceshi1(arr);ceshi2(arr);return 0;
}

  正如我在代码页所写的,两种形式均可,可以按着自己习惯来进行书写,第二个可以更好的展示为什么字节数是4 / 8个,因为这本来就是计算地址的长度!

小结:

  对于这部分的知识点,大家一定要好好掌握(客套话),下面进入二级指针(冒泡排序我觉着单独出一篇来写去,感觉写在中间有点显的它不重要) 。

4.冒牌排序算法的实现(下一篇见喽)

5.二级指针相关知识点

5.1.二级指针是什么

  我们知道,指针变量也是变量,同样也是有地址的,对于普通变量地址的存放是在指针里面,那么指针变量的地址应该存放到哪里呢,显然易见,它会放到二级指针里面,所以用大白话来讲,二级指针就是指针的指针,对于它如何进行初始化,下面献上代码:​​​​​​​

#include<stdio.h>
int main()
{int a = 20;int* p = &a;int** m = &p;   //可以这么记,有多少个*就代表着几级指针在初始话的时候return 0;
}

  正如我上问所说,经过我的观察,我发现*数决定着几级指针,其实二级指针目前我也没有设计很多,大多数题目设计到的都是一级指针,二级指针先知道大概就行,小编觉着这个在后期(数据结构,我还没学不知道大概)使用的应该更广一点。 

5.2.二级指针的解引用操作

  我们知道,在一级指针的时候进行解引用操作的时候,会直接取到这个数的本身,所以我们类比二级指针,在二级指针进行解引用的时候,会直接对应到一级指针的内容,也就是一级指针对应元素的地址,详情的代码如下:

#include<stdio.h>
int main()
{int a = 20;int* p = &a;int** m = &p;printf("%p\n", p);printf("%p", *m);return 0;
}

 

  从上面可以很显然的看出二者是相同的,这更加的证实了我上面所说,对二级指针进行第一次解引用的时候,得出的是一级指针的详情内容,划重点(第一次),所以有一必有二,二级指针也可以进行二次解引用,试想一下,第一次解引用的时候取到的是一级指针,对二级指针进行解引用的时候就是对一级指针再次进行解引用,得到了数的本身,很有趣吧,知识都是环环相扣的,下面是代码展示:

#include<stdio.h>
int main()
{int a = 20;int* p = &a;int** m = &p;**m = 12;printf("%d\n", a);printf("%d\n", **m);return 0;
}

 

  上面的代码很好的证实了我刚才说的正确性,所以二级指针是可以进行二次解引用的,当然,除了二级指针,还有三级指针,四级指针等等 ,不过这些都不经常使用,所以,指针记住一级指针=和二级指针就好,我们记住对我们有用的,记太多是容易遗忘的。

小结:

  二级指针的知识已经全部都讲完了,希望读者朋友们好好的理解,下面进入最后一小节喽!

6.指针数组相关知识点 

6.1指针数组

  在讲到这个之前,记住这是指针数组,一定要跟后期的数组指针区分开, 这俩真容易弄混。好了,进入正题,指针数组这个名字,可以类比记忆,类似整型数组,浮点型数组,顾名思义,这是个存放指针的数组,数组中的每一个元素都是指针(地址),下面来介绍一下它是如何进行初始化的:

int * arr[5] = {0};

  int代表的是类型,这一个不唯一,我只是当作例子展示一下,也可以是float,double等等,* 代表的是指针类型,arr是指的名字,arr[5]代表着存了五个地址,一定要记住它的写法,后期有很多指针类型等着我们记,我们一定不要搞混,这便是指针数组的初始化,下面可以进入一个小小的案例:用指针数组来模拟实现二维数组(小重点)! 

 

6.2.利用指针数组模拟二维数组 

  二维数组我们都知道(这部分我没有出博客,因为我有点懒),我们可以通过我们刚刚了解到的指针数组来进行模拟实现二维数组,下面先呈上代码供各位观看(代码下面是讲解

#include<stdio.h>
int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 1,2,3,4,5 };int arr3[5] = { 1,2,3,4,5 };int* arr[] = { arr1,arr2,arr3 };   //存放的都是数组第一个元素的地址,存放地址的叫做指针数组int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", *(*(arr + i) + j));    //也可以写成arr[i][j]//上面这个是挨着访问的,具体理解我写到博客里面,在这里我不好展开}printf("\n");}return 0;
}

 

  下面我们来对上面的代码进行更详细的解释 ,首先我们先要设置三个数组,里面都放着想对应的元素,是为了后续二维数组更加的完善,我们先把每个数组的首地址放到指针数组里面,为了可以把每个元素都打印出来,我们设置了两层循环,外层循环三次的原因是因为先选好每一个数组是什么,就比如i是0的时候,这时候开始访问第一个数组的地址,我们之后在进行循环就可以打印出第一个数组的内容了,这里的知识点涉及到了上面小编写过的《使用指针访问数组》就写到了如何用指针来写出数组,之后每次i++就会打印每一行数组的内容了,然后我们便模拟出了二维数组,小编画了两张抽象图片供读者理解:

 

  这个图虽然有点抽象,但我也画出了大概的雏形,就比如右图代表着设置了三个数组,左边代表着指针数组里面包含着三个数组首元素的地址。

小结:

  数组指针是指针中较为重要的知识点,读者朋友们一定要好好的领悟这个,不然后期真的很容易和数组指针弄混,小编建议大家可以看完文章后敲一下我在这篇文章写过的代码一定会有收获的!

总结:

  可算是写完这篇文章了,这篇文章可以看作是指针的进阶篇,指针从这便开始上强度了,大家一定要好好的学会指针,学会指针C语言便会打下半壁江山,加油,以后大厂的程序员们!如果文章出现了差错,欢迎从评论区指出,希望各位可以多点点赞,您的点赞便是我进步的动力,我们下一篇见啦!

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

怎么提升机器人外呼的转化效率

在某些情况下&#xff0c;如市场调查、产品推广等&#xff0c;语音机器人可以高效地完成大量的呼叫任务&#xff0c;并能通过预设的语音脚本和智能识别功能&#xff0c;初步筛选和分类潜在客户。此时&#xff0c;不转人工可能更为高效和经济。 然而&#xff0c;在一些需要深度沟…

停止游戏中的循环扣血显示

停止游戏中循环扣血并显示的具体实现方式会依赖于你的代码结构和游戏的逻辑。通常情况下&#xff0c;你可以通过以下方式来实现停止循环扣血和显示&#xff1a; 1、问题背景 在使用 Python 代码为游戏开发一个生命值条时&#xff0c;遇到了一个问题。代码使用了循环来减少生命…

【PLG洞察】|向Figma学习如何打造标杆客户和实施分销策略

Figma是一款功能强大的在线协同设计工具&#xff0c;它主要被用于界面设计、原型设计和用户体验设计。作为国外知名的saas企业&#xff0c;对标国内的saas蓝海&#xff0c;它的增长实在惊人&#xff01;据称&#xff0c;Figma2020年的收入已达$75M, 2021年6月&#xff0c;美国的…

Kafka流计算培训:打造Kafka技术专家,引领大数据未来

Kafka流计算培训课程是一门旨在帮助大数据从业人员和欲从事Kafka技术的人员快速掌握Kafka核心技术的专业培训项目。 在这个3天的课程中&#xff0c;我们将全面细致地讲解Kafka流计算软件的配置、Kafka流计算开发和流计算管道设计等内容&#xff0c;让学员能够在实际工作中灵活…

C++的异常捕获

目录 C语言的异常处理方式 C的异常处理方式 异常的抛出与捕获 抛出与捕获原则 异常安全 C语言的异常处理方式 1、终止程序 常见形式&#xff1a;assert 缺陷&#xff1a;太过强硬&#xff0c;如果发生内存错误&#xff0c;或者除0语法错误等就会直接终止程序 2、返回错误码…

2024年新闻传播、社会科学与公共艺术国际会议(ICJSSPA2024)

2024年新闻传播、社会科学与公共艺术国际会议(ICJSSPA2024) 会议简介 2024年新闻传播、社会科学与公共艺术国际会议(ICJSSPA2024)即将在上海举行。这项国际活动将汇集来自世界各地的新闻和传播学者、社会科学研究精英和公共艺术领域的创新者。会议旨在搭建一个开放包容的交…

MySQL Online DDL原理解读

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

Idea jdk配置的地方 启动时指定切换的地方

jdk 配置的地方 项目sdk 所在位置 管理添加或删除的地方&#xff0c;增加后&#xff0c;可以在在上面切换 启动时指定版本

UE4中性能优化工具合集

UE4中性能优化工具合集 简述CPUUnreal InsightUnreal ProfilerSimpleperfAndroid StudioPerfettoXCode TimeprofilerBest Practice GPUAdreno GPUMali GPUAndroid GPU Inspector (AGI) 内存堆内存分析Android StudioLoliProfilerUE5 Memory InsightsUnity Mono 内存MemreportRH…

Spring Security 与 JWT、OAuth 2.0 整合详解:构建安全可靠的认证与授权机制

Spring Security 与 OAuth 2.0 整合详解&#xff1a;构建安全可靠的认证与授权机制 将 JWT&#xff08;JSON Web Token&#xff09;与 OAuth 2.0 整合到 Spring Security 中可以为应用程序提供强大的认证和授权功能。以下是详细的整合步骤和代码示例。 1. 引入依赖 首先&am…

FastDFS简介与调优

背景&#xff1a;FastDFS在公司使用多年&#xff0c;一直作为主要文件存储服务使用。经过多场景、多项目实战检验。稳定性、性能均满足实战要求。相关使用介绍及调优给大家分享一下。 1.简介 FastDFS是淘宝架构师_余庆用C语言编写的一款开源的分布式文件系统&#xff0c;源码目…

3d模型文件格式有那些,什么区别?---模大狮模型网

3D模型文件格式有很多种&#xff0c;每种格式都有其特点和应用场景。常见的3D模型文件格式包括OBJ、FBX、STL、3DS、DAE等&#xff0c;下面将逐一介绍这些格式的区别。 1. OBJ格式&#xff1a;OBJ是一种开放的3D模型文件格式&#xff0c;可以被几乎所有的3D软件所支持。OBJ格式…

乐鑫ESP32-C3芯片应用,启明云端WT32C3-S5模组:简化产品硬件设计

在数字化浪潮的推动下&#xff0c;物联网(IoT)正迅速成为连接现实世界与数字世界的桥梁。芯片作为智能设备的心脏&#xff0c;其重要性不言而喻。 乐鑫推出的ESP32-C3芯片以其卓越的性能和丰富的功能&#xff0c;为智能物联网领域带来了新的活力&#xff0c;我将带您深入了解这…

(源码)一套医学影像PACS系统源码 医院系统源码 提供数据接收、图像处理、测量、保存、管理、远程医疗和系统参数设置等功能

PACS系统还提供了数据接收、图像处理、测量、保存、管理、远程医疗和系统参数设置等功能。 PACS系统提高了医学影像的利用率和诊疗效率&#xff0c;为医生提供了更加准确和及时的诊断依据。它是医院信息化的必备系统之一&#xff0c;已经成为医学影像管理和传输的重要工具。 P…

使用asyncua模块如何在opcua框架的Server端添加方法及在Client端调用方法

1. 在opcua框架的Server端添加方法 参考文章&#xff1a; freeopcua调用方法输入参数| Python解析数组到输入列表 为OPC UA python服务器/客户端添加安全性&#xff08;异步&#xff09; OPCUA和asyncua — [3] 添加方法 OPC UA的Server端新增方法的关键代码如下&#xff1a;…

如何确保数据跨域交换安全、合规、可追溯性?

数据跨域交换是指在不同的组织、系统或网络之间进行数据的传输和共享。随着数字经济的发展&#xff0c;数据跨域交换在促进数据流通和创新融合方面发挥着重要作用。然而&#xff0c;这一过程也面临着诸多挑战和风险&#xff0c;例如数据安全、合规性、完整性以及责任不清晰等问…

PHP在线生成查询产品防伪证书系统源码

源码介绍 PHP在线生成查询产品防伪证书系统源码&#xff0c;源码自带90套授权证书模板&#xff0c;带PSD公章模板&#xff0c;证书PSD源文件。 环境要求&#xff1a;PHPMYSQL&#xff0c;PHP 版本请使用PHP5.1 ~5.3。 图片截图 源码安装说明 1.上传所有文件至你的空间服务器…

关于Unity四种合批技术详解

文章目录 一.静态合批(StaticBatching)1.启用静态合批2.举例说明3.静态合批的限制4.静态合批的优点缺点5.动态指定物品合批 二.动态合批(Dynamic Batching)1.启用动态合批2.合批规则3.举例说明4.使用限制 三.GPU Instancing1.启用GPU Instancing2.启用限制3.举例说明 四.SRP Ba…

使用Omnipeek进行Wifi/P2P抓包

前言 工作中解决Mirracast投屏连接失败的问题时&#xff0c;遇到了需要抓取wifi数据包的情况&#xff0c;记录一下配置和使用过程。 一、Omnipeek 的安装和配置 1.1 Omnipeek 安装 双击 setup.exe 进行安装 1.2 注册 注册时&#xff0c;版本写 71 &#xff0c;产生序列号和…

【JVM】JVisualVM的介绍、使用和GC过程

VisualVM介绍 VisualVM 是Netbeans的profile子项目&#xff0c;已在JDK6.0 update 7 中自带&#xff0c;能够监控线程&#xff0c;内存情况&#xff0c;查看方法的CPU时间和内存中的对 象&#xff0c;已被GC的对象&#xff0c;反向查看分配的堆栈(如100个String对象分别由哪几…