典中典,掌握了这几道题,指针就已经毕业

6 篇文章 0 订阅
2 篇文章 0 订阅
本文详细解析了指针在字符数组、二维数组和数组指针中的经典问题,通过sizeof求取数组大小,以及二维数组间的减法和多维指针的复杂运算,强调了正确理解和运用指针的关键性。
摘要由CSDN通过智能技术生成

哈喽,大家好啊,我是情谊,今天我们来讨论一下几道关于指针的经典例题,我相信,掌握了这几道题后,大家的指针已经基本掌握了,那么,话不多说,我们直接来看看吧!

首先,我需要向大家解释一下,这几道题非常的经典,我希望大家还是能够尽力掌握这些题型,然后我希望大家都能够知道:数组名只在两种情况下表示整个数组,其他所有情况下都表示首元素的地址,这两种表示整个数组的情况:1直接用sizeof求取数组的大小  2使用&符号时,如何解释这两种情况,我们在下面的题型中在进行说明。

一,字符数组

 1strelen求取数组长度

bfd8bf30e50d4176b30bed9454cc2d7b.png

大家先看看上面这个程序,我们创建了一个字符数组,然后求取不同情况下的字符长度。

先看第一个,printf("%d\n",strlen(arr)),在这个代码中,arr表示首元素的地址,数组中没有\0,那么就会导致数组越界访问,结果就会是随机值。

第二句,一样的方法,也是表示数组首元素的地址上加0,还是没有\0,越界访问。

第三句,我们要知道,size_t  strlen(const char * s),arr是首元素的地址,*arr则是对数组进行解引用,即*arr = 'a',但是这里我们使用的是a的ASCLL码值,是97,所以这句话的意思是求取指向97这个地址的空间的长度,但是97这个地址是不能够被访问的,所以这一步就会直接程序崩溃。

第四句,arr[1]表示的是arr的第二个元素,即是’b',ASCLL码值是98,也是不能够访问的,程序就会崩溃

第五句,&arr中&取出的就是arr这个数组中的所有的元素,但是也依然用首元素的第一个地址来表示,但情况不同的是,这个代表的是整个数组的情况,操作时,也会进行整个数组的操作,所以在这个代码中,&arr传入的还是首元素的地址,所以求取的还是从第一个元素向后\0个元素,故值是随机值。

第六句,&arr表示的是整个数组,在整个数组的情况下加上一个一,就直接跳到数组最后一个元素的下一个空间的地址去了,即还是随机值,不同点是这个随机值是从数组的下一个空间算起的

第七句,&arr[0]+1,arr[0]表示的是字符‘a',&arr[0]则表示将a的地址取出来,然后加上一,也是随机值,不过是从数组的第二个元素向下数的。

这道题,我们从不同的方向介绍了什么情况下数组表示整个数组,什么情况下数组表示首元素地址,在数组中,上面的规则统一适用!

2sizeof求取数组大小

88d29b4be89647799686594fced95727.png

为了巩固数组关于指针的知识,我们接下来看看上面这个代码。

第一行,arr单独遇到sizeof表示的是整个数组,即求取的是整个数组的大小,包括\0,即结果为7

第二行,arr+0不再是单独的数组名遇见sizeof,所以在这里表示的是数组首元素,arr+0表示的还是数组首元素地址,所以求取的大小为指针的大小,结果是4/8

第三行,arr表示的是首元素的地址,*arr对首元素的地址解引用得到的就是‘a',结果是1

第四行,arr[1]表示的是’b‘,所以结果是1

第五行,&arr表示的是整个数组的地址,所以还是求取的指针的大小,故是4/8

第六行,&arr代表的是整个数组,所以&arr+1就会跳过整个数组,指向数组最后一个元素的下一个地址,地址就是指针的大小,所以值也是4/8

第七行,&arr[0]+1代表的是取出arr[0]的地址进行加一,故求取的是第二个元素的指针大小,结果还是4/8

综上上面这两道题,我们可以看出指针在数组上的特点,学习指针我们一定要区分清楚这些易错的问题!

二,二维数组

1sizeof求取二维数组的大小

e290861cce0c4f20b2835244745f09a3.png

再看上面这个代码之前,我们需要知道,二维数组本质上是由一维数组组合成的集合,其具体表现方式如下图,需要注意的是,我们可以将二维数组想象成为一个洋葱,剥开他的一层就是一维数组的集合,再剥开一层就是每一个元素的集合,也可以理解为,二维数组的元素就是一维数组。

0ab71f24fbf440f2b280dc63422b0ea6.jpeg 

那么我们接下来看代码

第一行,a直接遇见sizeof表示的是整个数组,故求取的是整个数组的大小

第二行,a[0][0],表示的是二维数组的第一个元素,求取的是该值的大小

第三行,a[0]表示的是二维数组的第一个元素的地址,上图的a[0]这一个一维数组的地址,但是地址的大小是固定的,为4/8

第四行,因为二维数组的元素是一维数组,所以a[0]+1跳过的是一个一维数组的长度,但是,依然是地址,所以大小依然是4/8

第五行,*(a[0]+1),再第四行的基础上解引用, 求取的就是a[1]这个一维数组的大小了

第六行,(a+1)中的a表示首元素的地址,+1就是a[1]的地址了,但是此处依然是地址,大小是4/8

第七行,*(a+1)是在第六行的基础上解引用,求取的就是a[1]整个一维数组的大小了

第八行,&a[0]+1,将a[0]的地址取出加上一,表示的是a[1]的地址,但是地址依然是4/8

第九行,*(&a[0]+1)是将a[1]解引用,求取a[1]的大小

第十行,*a,a表示首元素的地址,那么*a就表示首元素,即是a[0],所以求取的就是a[0]的大小

第十一行,a[3],上面代码中没有a[3]的数组,只有a[0],a[1],a[2],所以这里的地址相当于野指针,但是依然是地址,所以不管怎么样,地址的大小都是4/8

2二维数组之间的减法

bedc0d82ab3641b786775b4484676da3.png

在上面的代码中,a、是一个二维数组,p是一个数组指针,p=a表示的是将a中的首元素的地址给p,所以p存储的就是a[0]的地址,p[4][2]也可以写成*(*(p+4)+2), p是a[0]的地址,但是注意!!p+1加的是p类型的长度,因为p的类型是数组指针类型,结合该题所以加一就是加上一个四,如下图,所以*(*(p+4)+2)的值就是图中紫色的图形,同理,a[4][2]的地址就是绿色的图形,所以两者相减,计算的就是两者地址之间的元素个数

85550ece2d6b43a2bad38dd3964ad32f.jpeg

 

上面这一道二维数组的指针题目希望大小一定好好理解一下,一定要知道这些不同求取,让你求的是什么!

三,数组指针

2e53e433909048e49ddef99a7e2ffd8c.png

上面这个是一个简单的数组指针类型的题,我们来看一看吧

a是一个数组,&a则表示的是取出的整个数组的地址,&a+1则就是指向a最后一个元素的下一个空间的地址,但是我们注意到,&a+1的类型是int*[ ] ,表示的是数组指针,而ptr的类型则是int*,所以直接将&a+1的结果传给ptr就会报错,所以我们要将&a+1强转为int*才能赋值,接着,我们计算      *(a+1), a表示首元素的地址,+1则表示第二个元素的地址,再进行解引用就是a[1],结果是2。

*(ptr-1)中ptr是a[5]的地址,-1之后就是a[4]的地址,解引用后结果就是5

 

 

df6f2da5154e4e398c839589c4d2dec5.png

在这个代码中,我们主要区分ptr1和ptr2的区别,我们先来看ptr1

ptr1如同上一个代码一样,&取出的是整个数组,&aa+1就是数组最后一个元素的下一个地址

ptr2也是一样的分析方法,aa表示首元素的地址,即是a[0]的地址,a[0]+1就是a[1]的地址

所以接下来打印*(ptr1 - 1)的答案就是10,*(ptr2 - 1)就是5

还是需要注意,在ptr1和ptr2赋值的时候,将&aa+1和*(aa+1)的类型已经改变,所以执行加一和减一的操作时加减的字节数需要改变,也就是说,每次执行的都是该数据类型的长度!!

四,多维指针

6a55030d0c624e34b3446e5dc039090c.png

上面的代码中a是一个指针数组,存放的是三个字符串的首元素地址,如图

21a152f331aa45e2b1085f6abe70d5d6.png

pa是一个二级指针,存储的是a的首元素的地址,pa++跳过一个元素,指向a[1]的地址,再进行解引用就打印出at的地址了 

 

最后,我们再来看一个非常难的题

d5a1cf123bde47019a0108e5fab17744.png

这道题,也算是一道非常难的题,所以希望大家能够严正以待,其实对待这些题,我们最好的处理方式就是画出程序运行图。

8e181cb1547b46a8bd10b3c53b132aa1.png 

结合上面我们一直强调的知识,我们画出这个代码的运行图,接下来,我们看操作

第一个操作,**++cpp,表示先对cpp进行加一操作,再解引用,cpp表示cp的首元素地址,+1就指向了c+2的地址,解引用求出的是POINT.

第二个操作,*-- * ++cpp + 3,在这个操作中,我们要知道,加法是最s算的,所以先算++cpp

但是还有一个特别注意!!!前置加加会改变数据,即++cpp运算后的值是会重新赋值给cpp的,所以在第一次操作运算完后,cpp指向的应该是c+2的地址!!

++cpp让cpp又指向c+1的地址,进行解引用,就是拿到c+1这个数,再进行--(前置减减)操作,算出就是c,接下来继续解引用,就是NETER的首元素地址,最后进行加三的操作,即N的地址加三,打印出来的就是ER.

第三个操作,*cpp[-2]+3,可以换算成*  *(cpp-2) +3,那么像上面的运算方式一样,在上面的代码运算中,cpp指向的是c+1的地址, 所以cpp-2就重新指向c+3,再进行解引用,找到了c+3,再对c+3进行解引用,找到的是FIRST中的F的地址,最后进行+3,打印的就是ST

第四个操作,cpp[-1][-1]+1,可以转换为*(*(cpp-1)-1)+1,上面的cpp还是指向c+1的位置,只有前置加加和前置减减会改变数值,所以cpp-1就指向c+2,解引用后再减1,就变成了c+1,那么这个代码就变成了*(c+1)+1,然后再解引用,得到NEW首元素的地址,最后+1,打印的就是EW.

 

好了,以上就是目前我认为非常经典的一些关于指针的题型,我希望大家一定要好好理解这几道题,特别是最后一道题,一定要根据运行图去看代码,去看我的解读,不然很可能会搞不懂!!

看到这里的友友能不能给我一个点赞呢?你们的点赞就是对我最好的支持,如果上面的题中出现了部分错误,还请见谅啦,我还是会继续努力的,谢谢大家,我们下周见!

对了,最近有铁铁问我怎么学习编程语言,如果大家想要系统性学习编程语言而找不到合适的课程的话,也可以私聊小编哦,小编可以推荐一些小编认为比较好的课程哦。

 

 

  • 42
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值