有关于指针的基本知识点我们都已经讲解完了,不出意外的话,这一篇就是我们指针的最后一期了,今天我们就来看一看一些有关于指针运算的例题:
每一道题诸君都应该有着自己先独立思考的能力,咱们不要怕面对,不管分析的对不对,努力去迈出那第一步,咱们就已经成功了一半了,加油加油加油!
题目一:
解析:
第一行咱就不说了昂,整数数组嘛,我相信能看到这一期的诸君已经能够一瞬间理解第一行了。第二行:先取出 a 的地址,这个时候数组名 a 表示的是整个数组的地址,再 + 1 就是跳过整个数组,这个时候地址指向的就是紧接着数组后面的这个位置。(int*)的含义就是强转,将数组第二个元素的地址强制类型转换为 int 类型,然后再将其赋给我们的指针变量—— ptr,接下来就是打印了
(1)*(a + 1):这里的数组名 a 就表示首元素地址,+ 1 就是第二个元素的地址,再解引用,打印的就是 2
(2)*(ptr - 1):上面分析了指针变量 ptr 里存放的是整个数组后面这个位置的地址,- 1 自然就是向后移一位,对其解引用自然就是 5
题目二:
解析:
上面这个结构体的最后,是将这个地址—— 0x100000 先强转为 struct test 类型,再赋给我们创建的指针变量 *p
第一个打印: p 是我们的指针变量,里面放着我们赋值的 0x100000,加一个0x1,虽然这个数是16进制位,但 16 进制的 1 依旧是 1 ,又由于我们的指针变量 p 是一个结构体指针,我们这里设定的这个结构体大小为 20 字节,+ 1 跳过的是一整个结构体,也就是 20 字节,那么 20 字节在16进制中的表示是什么呢?是 14,所以这里的打印结果应该是 0x100014
第二个打印:将我们的 p 强制类型转换为 “ unsigned long ” 类型。这是整型类型,我们再加 0x1 也是整型,所以直接相加。打印结果为 0x100001
第三个打印:将我们的 p 强制类型转换为 “ unsigned int* ” 指针类型。这里的加 0x1 的意思是加上一个整型类型,打印结果为 0x100004
题目三:
解析:
创建了一个二维数组,再创建一个指针变量 p ,将数组首元素地址赋值给我们的变量 p ,再将其打印出来。这道题大家可以先思考一下,可能很多人得出的答案都是:0,心想这么简单的的题,一下就算出来了。众所周知昂,越是简单的题越是藏着坑,这题也有一个小坑,那就是关于二维数组内元素的表现形式,我们二维数组元素内,是可以使用括号来更明显的分段帮助理解的,但是呢,我们应该用大括号——{},而不是小括号——(),我们小括号的运算逻辑是从左向右依次运算,如果有多个运算结果,那么只取最右端为结果,所以我们二维数组内实际的元素只有三个:1、3、5,那么我们的首元素自然就是 1 了
我们所想的真正的二维函数是这样的:
题目四:
解析:
这里创建了一个二维数组,紧接着创建一个数组指针,将二维数组名(首元素地址,也就是这个二维数组第一行的地址)先强转为指针数组类型,再赋值给我们的数组指针变量 p ,且 p 每行是 4 个整型大小。接着就是打印,前一个为地址打印,后一个就是普通打印
我们的二维数组在内存中实际上是连续储存的,所以为了更好的理解,我们可以将其看做是一个一维数组的格式。我直接在图上给大家演示:
有图可知,我们找出了p [4] [2] 和 a [4] [2] 所指向数组中的位置
我们再来看需要打印的:&p [4] [2] - &a [4] [2] = - 4(地址相减得到的是地址之间的元素个数)
由于第一个打印是地址,那么我们 - 4 的地址是什么呢?这个就要涉及到我们的原、反、补了
综上所述,我们最终的打印结果应该是:
题目五:
解析:
首先给出一个二维数组 aa [2] [5] ,然后设立了两个指针变量 ptr1、ptr2,我们分别来看
ptr1:&aa,取出二维数组aa的地址,这里取出的就是整个二维数组的地址,+ 1 自然就是跳过整个数组,也就是说此时我们的 ptr1 指向的是我们二维数组后的那个位置
ptr2:单独出现数组名,没有sizeof,所以这里就是我们的首元素地址,就是 aa [0],在二维数组中的第一行地址,再 + 1 自然就是 aa [1] 咯,也就是第二行第一位元素的地址
有细心的小伙伴可能发现了,在赋值给指针变量的时候还有一个 int * 的强转,不过这个强转没啥用哈,这个数组本身就是 int 整型的
最后我们来看一下打印结果:
题目六:
这里编译器报警告了昂,这是因为我使用的是C++文件,程序审核比较严格,我们题目是将三个字符串放进这个指针数组里面,原则上这是不被允许的
解析:
这个题涉及到二级指针,我们干看着不太容易理解,我直接在图上为诸君解析一下:
我们将这字符串强行赋给指针数组中,实际上是存放的这三个字符串首字符地址,类型都是 char*,然后再将数组 a 赋值给我们的二级指针 pa ,a 是数组名,也就是首元素地址,所以这里 pa 里存放的是 a 中下标为 0 的地址,pa++ = pa+1,也就是跳过一个字节,此时指向了我们下标为 2 的位置,对其解引用得到了第二个字符的首元素地址,再将其打印,又因为我们的字符串的末尾是隐藏着一个 '\0' 的
所以最终的打印结果就是:at (这里因为编译器会报错,所以我就不打印了)
题目七:
解析:
对于题目中各个指针数组的解析,上面那题讲解的很清楚了,咱就不过多赘述了
* * + + CPP:这里是前置 ++ ,也就是先做 ++ 运算,再对其解引用,本来的 cpp,应该是指向的指针数组 cp 中的首元素,++ 过后,跳过一个字节,此时指向的第二个元素,也就是 C + 2 的下标,对其一次解引用就是 cp 中的 C + 2 ,二次解引用就是 c 中的第三位元素的地址 —— p 的地址,所以打印结果应该为:POINT
* - - * + + C P P + 3:这一个运算就比较复杂了,涉及到我们 C 语言运算符的优先级考虑,之前出过两期操作符详解,感兴趣的小伙伴可以去回顾一下哈:C语言中操作符详解(二)-CSDN博客
言归正传,首先,我们的三级指针 cpp 在第一个打印中已经经过了一次运算:++ cpp,这个运算是会保留的哈,并不是说只是走个过程,所以我们在第二个打印这里的 cpp 已经是指向了 C + 2 的位置,然后考虑我们的运算符优先级,++、-- 的优先级是最高的,然后是解引用 *,再是普通的+。按照这个顺序我们来思考。先做 ++ ,此时指针来到我们的 C + 1 位置,对其解引用,找到这个元素: C + 1,再进行 - - ,这不就是 C 了么。再对其解引用找到字符串 “ ENTER ” 首字母 E 的地址,最后 + 3 就是跳过三个字节来到倒数第二这个 E ,然后打印,所以最后的打印结果就是:ER
* C P P [ -2 ] + 3:这个就更有意思了,下标出现了一个 - 2,大家可能看的一头雾水,心想,这是什么啊?哪还有下标为 - 2 的道理哦。其实道理嘛,万变不离其宗,咱们得透过表相看本质,只要我们来举个简单的例子,聪明的诸君立马就会如醍醐灌顶般理解了:
arr [ 0 ] 是不是就等于 * (arr + 0),同理,arr [ 1 ] == *(arr + 1)
那么 arr [ -2 ] == *(arr - 2)
诸君是不是一下就明白了
OK,咱们继续,由于经过上面的两次计算,此时我们的 cpp 已经指向了 C + 1 的位置,在这个基础上 - 2 也就是回到了 C + 3 的位置,对其解引用也就是找到了我们 c 中 F 的位置,再 + 3 就是向后跳过三个字节,所以最终的打印结果为:ST
C P P [ -1 ] [ -1 ] + 1:这里又出现了二维数组的形式,不过不要怕,咱们一层一层地来剖析它。有的小伙伴看到二维数组的形式就怕了,不要怕嘛,这些都是纸老虎,经过我们上一道题的解析,再来看这个不也是一样的原理么:c p p [ -1 ] [ -1 ] == *(*(c p p - 1)- 1),我们此时的 c p p 可还是指向的 C + 1 的位置,因为第三次打印不是自加或自减的形式,其运算过程并不会对其本身造成改变,所以先 - 1 指向了 C + 2 的位置,再 - 1 得到 C + 1,然后再解引用找到我们 c 中 N 的地址,再 + 1 跳过一个字节,所以最终打印结果为:EW
OKK,咱们有关于指针的知识点到此就告一段落啦,这部分很难理解,文字毕竟是生硬的,大家应该多找一些网课看一看,并且自己多加练习,我也会继续努力,咱们一起加油!好了,那咱们闲话少说,下期再见吧,与诸君共勉!!!