前言:

 🚪 传送门:指针初阶_醉酒笑清风的技术博客_51CTO博客

 🚪 传送门:指针进阶(上)_醉酒笑清风的技术博客_51CTO博客

 🚪 传送门:指针进阶(下)_醉酒笑清风的技术博客_51CTO博客

本章内容主要讲解指针练习题,里面的内容是我对这些练习题的一些理解。如有对C语言指针有疑问,可以查看我之前对指针介绍的三篇文章,希望可以帮助到大家。


题目一:

C语言指针题目详解_指针数组

解答:

数组如图1.1所示,下面分布解释代码:

  1. int a[5] = {1,2,3,4,5};创建一个存放5个元素的数组,数组名为a,数组元素为1,2,3,4,5。
  2. int* ptr =(int*)(&a + 1);我们拆解来看:

1)&a,取出整个数组的地址。

2)&a+1,是跳过当前数组指向数组后方的位置。

3)(int*)(&a+1)则是将此处位置的指针强制类型转化为int*。

4)int* ptr是创建一个int*类型的指针变量ptr用来存放&(a + 1)的地址。

  1. *(a + 1)等价于a[1],即数组的第二个元素。
  2. ptr - 1相当于ptr这个指针向后移动一位,指向元素5处。*(ptr-1)则是将ptr这个指针解引用得到的是数组中的元素5。
  3. 综上所述,代码的执行结果为: 2, 5

C语言指针题目详解_数组_02

图1.1

C语言指针题目详解_指针数组_03


题目二:

C语言指针题目详解_数组_04

解答:

  1. 输出函数中要输出p + 0x1,我们规定p的初始值为:0x100000,并且题目中明确给出结构体的大小,此时的p 加上的0x1,加上是一个结构体的大小,因为p是一个结构体指针,对于指针加减是加减上一个指针类型的,即int* + 1,即跳过一个整形4个字节。char* + 1,即跳过一个字符型1个字节。所以结构体指针+1,跳过一个结构体20个字节。%p是以16进制打印,所以输出结果应为: 0x100000 + 14 = 0x100014。
  2. 对p强制类型转化为一个无符号整形,此时对p + 0x1,即p是整形类型,再加整形数字1,所以输出结果应为0x100000 + 1 = 0x100001。
  3. 对p强制类型转化为无符号的int*类型,此时对p + 0x1,即加上一个int*类型4个字节,所以输出结果应为: 0x100000 + 4 = 0x100004

C语言指针题目详解_二维数组_05


题目三:

C语言指针题目详解_指针数组_06

解答:

如图1.2所示,下面分布拆解代码:

  1. &a取出的是整个数组的地址,&a+1,是跳过当前数组指向这个数组后方的位置,即图中所示的位置。
  2. (int*)(&a + 1),将此处的地址强制类型转换为int*,并存放于int* 类型的指针变量ptr1中。
  3. a是数组名,代表数组首元素的地址,(int)a则是将数组首元素强制类型转化为int类型,如图中示例,假设a的地址为0x1122ff40,则(int)a后0x1122ff40就不是地址而是整形数字。
  4. (int)a + 1,即0x1122ff40 + 1 = 0x1122ff41。
  5. (int*)((int)a + 1),强制类型转化为int*类型,此时的0x1122ff41不再是整数,而是又变为了地址,地址指向0x1122ff41这个位置。
  6. 将(int*)((int)a + 1)的值存放于int*类型的ptr2中。
  7. ptr[1]的位置如图中所示。
  8. %x是以16进制形式打印该结果。

C语言指针题目详解_二维数组_07

图1.2

C语言指针题目详解_指针数组_08


题目四:

C语言指针题目详解_数组_09

解答:

这里有一个小陷阱,如果想规定显示每行放什么内容的话,需要在{}内部再嵌套一层{},如:

{{0,1}, {2,3}, {4,5}}这样显示的才是将0,1放在第一行,2,3放在第二行,4,5放在第三行。而题中的是三个逗号表达式,最终计算的结果为:{1, 3, 5}。

下面我们拆解分析代码:

  1. 由于{}内部是逗号表达式,所以存放在内存中的实际元素应该是如图1.3所示。
  2. 在二维数组中,有如下情况:

1)a[0]就是第一行一维数组的数组名,举例: a[0][j],j:0~1

2)a[1]就是第二行一维数组的数组名,举例: a[1][j],j:0~1

3)a[2]就是第三行一维数组的数组名,举例: a[2][j],j:0~1

  1. 创建一个int*类型的指针变量p,将二维数组中第一行一维数组存放在里面。
  2. 此时的p指向二维数组第一行的一维数组,p[0]等价于*(p + 0),二者哪个能看懂就看哪个。此时的p相当于二维数组第一行的一维数组的数组名,数组名[下标]访问的就是这行的第几个元素。
  3. 所以,结果应为: 1。

C语言指针题目详解_指针数组_10

图1.3

C语言指针题目详解_指针数组_11


题目五:

C语言指针题目详解_二维数组_12

解答:

  1. 创建一个5*5的二维数组a,再创建一个int(*p)[4]类型的数组指针。
  2. 将二维数组a存放在数组指针p中,由于二维数组中每一列存放的元素个数为5个,而数组指针规定它只能指向存放4个元素的数组,所以数组会被分为如上图1.4中所示的4个4个为一组的情况。
  3. p[4][2]存放的是被切割为4个4个为一组的形状,第五组中的第三个元素,即图中所示的p[4][2]的位置,而a[4][2]是原本二维数组a中的第5组第3个元素。
  4. &p[4][2]和&a[4][2]分别得到的是该位置的地址。
  5. 我们先看%d输出的,这是明显的指针±指针,得到的结果是两个指针之间的元素个数,所以&p[4][2]-&a[4][2]的结果为: -4。
  6. %p是以地址的形式打印指针-指针的结果,由于规定随着数组下标的增长地址是由低到高变化的,所以%d的输出结果为-4,-4是将内存中的数据按照原码的形式输出的,而%p输出的是将计算结果以地址的形式输出,所以-4在内存中的存储形式如下:

1)-4原码: 1 0000000 00000000 00000000 00000100

2)-4反码: 1 1111111 11111111 11111111 11111011

3)-4补码: 1 1111111 11111111 11111111 11111100

  1. 虽然内存中是这样存储的,既有符号位又有数值位的,但是我们要打印的是地址,没听过谁家的地址还有正负之分。所以这里将最高位的符号位也归为数值位中,而地址在内存中是以16进制显示的,(对应关系为1个16进制位 = 4个字节), 并且在内存中是按照补码形式存储的。所以-4的补码转化为16进制的结果就是: FF FF FF FC

C语言指针题目详解_二维数组_13

图1.4

C语言指针题目详解_二维数组_14


题目六:

C语言指针题目详解_数组_15

解答:

  1. &aa,取出的是整个数组的地址,&aa+1,是跳过当前整个数组指向整个数组后面的位置,即图1.5所示。
  2. 再将&aa+1所处位置的地址存放在ptr指针变量中。
  3. aa是数组首元素的地址,二维数组中数组首元素代表的是第一行的一维数组的地址。aa+1则是代表第二行的一维数组的地址。*(aa+1)等价于aa[1],即第二行数组的数组名。数组名有相当于数组首元素的地址,即表示为: aa[1][0]。即元素6的地址。
  4. 将*(aa+1)强制类型转化为int*类型(由于*(aa+1)得到的本来就是地址,这里再强制转换没有意义),存放在ptr2中。
  5. ptr-1指向的是二维数组中元素10所在的位置地址。ptr2-1指向的是二维数组中元素5所在位置的地址。
  6. 分别对其解引用得到的结果为: 10, 5。

C语言指针题目详解_指针数组_16

图1.5

C语言指针题目详解_数组_17


题目七:

C语言指针题目详解_二维数组_18

解答:

  1. a是一个字符数组,数组中存放的元素是char*类型的,即每个数组的首元素的地址。
  2. a是数组首元素的地址,即第一行字符数组的地址。将a指向的地址存放在char**pa的二级指针中。
  3. 对pa++,对指针++得到是指针向后移动一个指针类型的距离。即指向at所在的字符串。
  4. *pa得到的是at这个字符串首元素的地址,使用printf函数打印,打印结果为: at。

C语言指针题目详解_二维数组_19

图1.6

C语言指针题目详解_指针数组_20


题目八:

C语言指针题目详解_指针数组_21

C语言指针题目详解_数组_22

图1.7

解答:

根据前三段代码可以得出一幅图片,即图1.7。

下面我们分布拆解代码:

  1. ** ++CPP, CPP指向的是CP数组中的第一个元素,指针数组CP存放的是指针数组C中的地址,即如上图1.7中所示。
  2. ++CPP,是跳过一个char***类型,指向下一个位置,即如下图所示:

C语言指针题目详解_指针数组_23

  1. ** ++CPP, ++指针完成下一步是对指针进行解引用,第一次解引用得到的是指针数组中的第二个元素CP[1], 再次解引用,我们通过指针数组CP[1]得到指针数组C中的第三个元素C[2],C[2]中存放的是字符串"POINT"的首地址。
  2. 通过printf函数输出得到就是: POINT。

 

  1. *-- *++ CPP +3,我们对这段代码进行拆解:

1) 由于C语言中按照操作符优先级的顺序,++、--的优先级最高,其次是*,最后是+运算符。

2)由于是前置++,要先完成++操作后才能进行解引用操作。而且++操作可以实现对指针的移动,所以目前的CPP指针是指向CP[1]位置的。当++CPP后,CPP指针指向下一个位置,如图所示:

C语言指针题目详解_数组_24

3) ++CPP完成后,对其解引用,解引用得到指针数组CP中的第三个元素即CP[2]。

4)再计算-- * ++CPP,由于题目中表明,CP[2]中存放的是C+1,这时对CP[2]中的值--,得到的就是C,即图中指针数组C中红圈的位置。

5)再对其解引用得到的就是字符串"ENTER"中的首元素的地址。

6)最后再+3,即从字符'E'向后移动三个字符,指向字符'T'后面的字符'E'。通过printf函数输出得到的结果就是: ER。

 

  1. *CPP[-2] + 3,首先先计算CPP[-2],CPP[-2]等价于*(CPP - 2),即CPP指针指向的是指针数组CP[0]的位置。如下图所示:

C语言指针题目详解_数组_25

  1. 在对其解引用*CPP[-2]后得到指针数组C中第4个元素即C[3],如图中指针数组C中红圈所示的位置。对该位置进行解引用得到的字符串"FIRST"的首地址。
  2. 最后再+3,即从字符"F"向后移动三个字符,指向字符's'的位置,通过printf函数输出的结果就为: ST。

 

  1. CPP[-1][-1] + 1,我们拆解后依次分析:

1)CPP[-1][-1]等价于*(CPP -1)[-1],也等价于*(*(CPP -1) -1),即先计算CPP-1,将CPP指针的指向位置向上移动一个单位,指向指针数组CP中第二个元素CP[1],然后对其进行解引用操作,得到指向指针数组C中第3个元素,即C[2]。

2)由于题中给出条件,CP[1]中存放的值为C+2,所以(CPP -1) -1的值为:C +1 ,即目前的指针指向如下图所示:

C语言指针题目详解_数组_26

3) 再计算*(*(CPP -1) -1),即对其解引用得到的就是指针数组C中的第2个元素C[1],C[1]中存放的是字符串"NEW"的首地址。

  1. 再+1,指针指向数组字符串中字符'E'的位置,通过printf函数输出,结果为: EW。

C语言指针题目详解_数组_27



总结:

C语言学习没有捷径,只学不练“假把式”,以上是我个人对八道题目的浅显理解,如有问题,请读者不吝赐教。

C语言指针题目详解_二维数组_28