赞
踩
指针试题1:
解析:
数组a是一个整型数组,数组有5个整形元素。
数组名a在大部分情况下表示的数组首元素的地址,除了两个情况
1:数组名单独作为sizeof操作数的时候,表示的是数组的大小
2:&数组名表示的是数组的地址
&a表示的数组的地址,&a + 1,即指向了数组最后一个元素的下一个内存单元的地址
从指针的角度看
指针的类型决定了指针+/-整数,跨越的距离,指针的类型是 int (*)[5], 表示其指向了一个含有5个整形元素的数组,prt + 1,跨域的距离是20字节
所以,实际上&a + 1就如下
然后将&a + 1强制类型转换成(int*)类型,赋值给int*类型的指针变量ptr,就相当于,ptr也保存了0x0000FF24这个地址,它会将该地址所指向的内存单元中的值以整形的角度去看待。
接下来就是*(a + 1),a是数组首元素的地址,a + 1实际上就是数组元素a[1]的地址,从指针的角度解读就是
指针的类型决定了指针+/-整数,跨越的距离,指针的类型是int*,表示其指向了一个整形,所以str + 1,跨越的距离是4字节,即指向了0x0000FF14,即a[1]的地址,
而*(a + 1) <==> *(str + 1) <==> *(&a[1]) <==>a[1]
所以*(a + 1)输出2
最后一个*(ptr -1),ptr-1,指针的类型决定了指针+/-整数,跨越的距离,指针的类型是int*,表示其指向了一个整形,所以ptr - 1,跨越的距离是4字节,即指向了0x0000FF20,
即数组最后一个元素a[4]的地址,同样的对其解引用,就得到了a[4]
所以*(ptr -1) 输出5
指针试题2:
解析:
0x1 <==> 0x00000001 <==> 1
指针变量p的类型是结构体指针类型,即struct Test*类型。并且,可以通过计算,得到一个结构体变量内存的内存空间的大小是20字节, 也就是指针变量p +/-1跨越的距离是20字节。
p+1, 指针变量p中存放的值是0x100000,即0x00100000, 指针变量+/-整数,跨越的距离取决于指针类型,由上述可知,跨越的距离是20字节,所以,实际上p + 1就指向了如下的地址。
所以p + 1指向的地址是0x00100014,然后将该值以十六进制打印出来,得到的结果就是00100014
(unsigned long)p + 1,将指针变量p强制类型转换成unsigned long类型,强制类型转换改变的只是我们看待它的方式,而并没有改变其在内存中的二进制值。指针变量在内存中存放的是地址,即0x00100000,所以将该值当成是无符号整形看待。
(unsigned long)p + 1就相当于两个整数相加
即0000 0000 0001 0000 0000 0000 0000 0000
+ 0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0001 0000 0000 0000 0000 0001
将该二进制值以%p(十六进制)的形式打印,得到的结果是 00100001
(unsigned int*)p + 1,将指针变量p强制类型转换成unsigned int*类型,指针的类型决定了指针+/-整数跨越的步长,p从struct Test*变成了unsigned int*,表明其指向了一个unsigned int类型的元素,所以其+1跨越的步长从20字节变成了4字节。
所以,p + 1实际上指向了如下位置
所以将0x00100004以%p(十六进制)打印到标准输出上,就是00100004
指针试题3:
解析:
数组a是一个有4个元素的整形数组。
&a表示的数组的地址,&a + 1表示的是数组最后一个元素下一个内存单元的地址。
从指针的角度看待就是
指针的类型决定了指针+/-整数跨越的步长,ptr指针的类型是int(*)[4],表明其指向了一个具有4个元素的一维数组,所以其+1跨越的步长是16字节,所以&a + 1如下所示
然后将&a + 1强制类型转换成(int*)类型,赋值给int*类型的指针变量ptr1,就相当于,ptr1也保存了0x0000FF20这个地址,它会将该地址所指向的内存单元中的值以整形的角度去看待。
然后ptr1[-1] 实际在底层被解析成 *(ptr1 -1),ptr1是int*类型,表示其指向了一个整形元素,所以ptr1-1跨越的距离是4个字节。所以等于指向了0x0000FF1C,也就是数组元素a[3]的地址。也就是说,ptr1-1之后,ptr1中保存的就是a[3]的地址0x0000FF1C,而对其解引用也就是取出了a[3]所在的内存空间的值,也就是a[3],所以以%x(十六进制)的形式输出,结果输出4
(int)a + 1, a是数组首元素的地址,即&a[0], 即0x0000FF10,将a强制类型转换成整形,那么就是将0x0000FF10的值当成整形看待,而该值+1,就相当于是
0000 0000 0000 0000 1111 1111 0001 0000
0000 0000 0000 0000 0000 0000 0000 0001
= 0000 0000 0000 0000 1111 1111 0001 0001 <==>0x0000FF11
然后将该值又强制类型转换成了(int*)类型,即将0x0000FF11作为地址,存放到整形指针变量ptr2中,如下所示
然后*ptr2,对指针变量解引用,也就是通过指针变量中存放的地址,找到该地址所在的内存空间,然后根据指针类型的大小,读取其对应权限大小的空间内容,ptr2是int*类型的指针,其操作权限是4字节,所以从0x0000FF11所在的内存单元开始,读取4个内存单元的值,
这个时候,就需要考虑到内存单元中存放的是啥,所以要考虑到大端存储和小端存储的问题
如下所示
如果是大端存储
那么,从地址0x0000FF11开始,在内存中连续的4个内存单元的值是00 00 01 00
按照大端存储的方式取出,即数据高位在低地址,数据低位在高地址,
还原原数据是:0x00000100
如果是小端存储
那么,从地址0x0000FF11开始,在内存中连续的4个内存单元的值是00 00 00 02
按照小端存储的方式取出,即数据高位在高地址,数据低位在低地址,
还原原数据是:0x02000000
所以,根据不同的机器,最后一个得到的结果以%x形式输出,得到的可能是
100, 或者2000000
指针试题4:
解析:
数组a是一个3行2列的二维数组,这里最大的一个坑,在于二维数组的初始化
可以看到,代码中的初始化是使用了逗号表达式,而逗号表达式的执行原理是从左到右执行每个表达式,最终的结果以最右边的表达式为准。
所以,上述初始化其实等价于
而如果真正想要使用上述012345完成完全初始化,那么需要如下方式
了解了坑所在,这道题就好做了,p[0]在底层是被解析成了 *(p + 0), 而p是一个整形指针,p指向了a[0], 即p中保存了a[0]的地址,a[0]是二维数组的首元素,即可以看成是一个一维数组的数组名。
数组名表示的是数组首元素的地址,所以,相当于指针变量p指向了a[0]数组的首地址,
相当于&a[0][0],所以,p + 0,还是等于p,然后解引用就得到了a[0][0],也就是1
指针试题5:
解析:
看到这道题要明确的第一个概念是,当两个指向同一块内存空间的指针相减,得到的是两个指针之间的元素个数。
数组a是一个5行5列的二维数组,其在内存中的分布可以如下所示
假设说a[4][2]的地址就是0x0000FF68,现在再来考虑指针变量p,可以看到,p是一个数组指针,其类型是int(*)[4],表示其指向的是一个含有4个元素的数组,其+/-1跨越的距离是一个数组的距离,即16字节。
p = a;这里a是二维数组的数组名,即其首元素的地址。数组a的首元素是一个一维数组a[0],a[0]相当于是一个一维数组的数组名,a[0]的地址,就是这个一维数组的数组地址,即&a[0], 但是因为数组在内存中是连续存储的,所以,实际上数组的地址,数组名(数组首元素的地址)实际上指的都是相同的一块内存单元的编号(地址),也就是如上的0x0000FF10,也就是p中保存了该地址。
p[4][2]在底层被解析成 *(*(p + 4) + 2), 通过p+4以及p的类型int(*)[4],可知p跨越了64字节,也就是来到了如下所示所在的内存单元
而*(p + 4)对其解引用,就得到了p[4], 在这里,需要扩展的一点是,p它不知道自己指向的是一个5*5的二维数组,它只知道自己指向的是一个含有4的元素的一维数组,所以,当p指向0x0000FF58这个内存单元的时候,它其实是把这个地址当成是一个含有4个元素的一维数组的地址去解读的,也就是从0x0000FF50作为数组地址开始,一直往后4个整形内存空间,在p看来就是一个数组,所以,*(p + 4)就相当于拿到了那个数组的数组名, 然后
*(p + 4) + 2 <==> p[4] + 2,这里p[4]既然数组的数组名,p[4]+2就相当来到了该数组下标为2的元素位置,也就是如下的位置
这里实在不明白,可以换成指针的角度看,指针p+4之后,指向了0x0000FF50这个内存的单元,因为p是一个数组指针,其指针类型是int(*)[4], 所以,在p认为,0x0000FF50这个地址就是数组的地址,所以,在p看到,上述空间就如下所示
从代码的角度看,完全可以看成这样
所以,*(p + 4)就等价于array, 所以 *(p + 4) + 2就等价于 array + 2,
所以 *(*(p + 4) + 2)在解引用就等价于array[2],而&p[4][2]就等价于&(*(*(p + 4) + 2)),即
&array[2],所以相当于是拿到了下标为2这个元素的地址
在图上可得,地址是0x0000FF58
现在就回到了地址相减的问题上,指向同一块内存的两个指针相减,得到的两个指针之间的元素个数,也就是0x0000FF58 – 0x0000FF68之间的元素个数,因为都是整形指针,所以,之间元素个数是4个,但是因为是低地址-高地址,所以结果是-4
然后就是将-4分别以%p和%d的形式打印,%d毋庸置疑,打印-4,而%p是十六进制打印
-4的补码是 1111 1111 1111 1111 1111 1111 1111 1100, 转换成十六进制就是0xFFFFFFFC
所以最终结果:0xFFFFFFFC和4
指针试题6:
解析:
数组aa是一个2行5列的二维数组,数组每个元素都有了确定值。
指针变量ptr1和ptr2都是一个整形指针,其+/-1跨越的距离是4字节。
&aa表示的二维数组其数组的地址,从指针的角度去理解就是
&aa+1就等价于 p + 1,指针p的类型是 int(*)[2][5],所以其跨越的距离是40字节,相当于跨越了一整个二维数组,指向了二维数组最后一个元素的下一个内存单元,如下所示
然后将(p + 1)强制类型转换之后,赋值给指针变量ptr1,因为ptr1是int*类型,所以,它会将该地址以整形的角度去看待。
*(ptr1 -1),ptr1是整形指针,跨越的距离是4字节,ptr1-1,就指向了aa[1][4]所在的内存单元,即保存了该内存单元的地址,然后解引用*(ptr -1),又因为ptr1是int*类型,其解引用的权限是4字节,所以就相当于拿到了aa[1][4]这个元素。所以输出10
*(aa + 1),这里aa是二维数组的数组名,表示其首元素的地址,即&aa[0],从指针的角度去理解,就是
所以 aa + 1 <==> p + 1, p的类型是int (*) [5],所以其跨越的距离是20字节,即指针指向了如下位置
该内存单元不仅表示aa[1]这个数组首元素aa[1][0]的地址,也表示了数组aa[1]的地址,但是因为p是数组指针,所以p+1,指向该内存单元之后,其看待该地址的方式就是一个数组的地址。
所以(aa + 1)就相当于拿到了aa[1]这个一维数组的地址,而*(aa + 1)就相当于该数组的数组名,aa[1]是一个一维数组,其数组名就是首元素的地址,首元素本来就是一个整形,所以强不强转无所谓,然后赋值给ptr2。
*(ptr2 - 1),同ptr1,指针变量ptr2是int*类型,其+/-1跨越的距离是4字节,所以相当于指向了aa[0][5]所在的内存单元,然后解引用就拿到了aa[0][5],所以输出5
指针试题7:
解析:
数组a是一个指针数组,数组一共有三个元素,a[0], a[1], a[2]
每个元素都是一个char*的指针,分别指向了常量字符串 “work”, “at”, “alibaba”,如下所示
char**pa = a; 这里a是数组名,表示的是数组首元素的地址,数组首元素是char*的指针,所以其地址可以用二级指针pa接收,也就是说,指针pa中存放了a[0]的地址0x0000FF10,
pa++,pa是char**类型,其+/-整数跨域的距离是一个指针所在内存空间大小,也就是
4字节,所以指向了a[1]所在的内存空间,也即是pa中保存了a[1]的地址0x0000FF14,
相当于
而*pa,就相当于得到了a[1], a[1]就存放的地址是0x00000F15, 也就是字符串”at”的首地址,将其以%s(字符数组)的形式格式化输出,就相当于输出了”at”
指针试题8:
解析:
直接画图,先明白指针数组c,指针数组cp,以及三级指针的内存分布
**++cpp,cpp中存放了数组cp的数组名,相当于保存了其数组首元素的地址,即0x00000FF0,
又因为++优先级高于*,所以先前自增,cpp类型是char***,其+/-1跨越的距离是4字节,所以相当于指向了0x00000FF4所在的内存单元。即
第一次*cpp,等价于拿到了cp[1], cp[1]是一个二级指针,其值是0x0000FFFC,是数组c的元素c[2]的地址,如果从指针看的话,如下所示
所以*(cp[1]) <==> *(cp_1) <==> c[2]
然后将c[2]中的地址0x00000F1传递给printf函数,以%s格式化输出字符串”POINT”
Ps:要注意 cpp指针经过这次的++之后,已经指向了cp[1]所在的内存空间,即cpp中保存的地址是0x00000FF4
*--*++cpp + 3,依旧是++优先级最高,所以先++,cpp类型是char***,其+/-1跨越的距离是4字节,所以相当于指向了0x00000FF8所在的内存单元。即
*cpp等价于拿到了cp[2], cp[2]是一个二级指针,其值是0x0000FFF8,是数组c的元素c[1]的地址,如果从指针看的话,如下所示
然后—cp[2] <==> --cp_2,这里cp_2是二级指针,其+/-1跨越的距离是一个指针所占用内存空间大小的距离,即4字节,所以,--cp_2相当于cp_2指针指向了0x0000FFF4所在的内存单元,即cp[0]的地址,从指针看的话就是
而解引用之后,就相当于拿到了c[0],所以*--*++cpp + 3变成了c[0] + 3,
c[0]是一个一级指针,其指向了一个常量字符串,从指针角度看的话,如下所示
c[0] + 3 <==> c_0 + 3,指针变量c_0是char*类型的指针,其+/-3跨域的距离是3字节,所以相当于c_0指向了字符串”ENTER”中第二个E所在的内存单元,相当于c_0中保存了0x00000F13, 然后将c_0中的地址0x00000F13传递给printf函数,以%s格式化输出字符串”ER”
Ps:要注意 cpp指针经过这次的++之后,已经指向了cp[2]所在的内存空间,即cpp中保存的地址是0x00000FF8
*cpp[-2] + 3,在底层被解析成 *(*(cpp -2)) + 3,这里cpp-2,cpp类型是char***,其+/-2跨越的距离是8字节,所以相当于指向了0x00000FF0所在的内存单元。即
然后解引用*(cpp -2 )就相当于拿到了cp[0],cp[0]是一个二级指针,其值是0x0000FFFF,是数组c的元素c[3]的地址,如果从指针看的话,如下所示
然后解引用,*(*(cpp -2)) <==> *(cp[0]) <==> *(cp_0) <==> c[3],相当于拿到了c[3],
c[3]是一个一级指针,其指向了一个常量字符串,从指针角度看的话,如下所示
c[3] + 3 <==> c_3 + 3,指针变量c_3是char*类型的指针,其+/-3跨域的距离是3字节,所以相当于c_0指向了字符串”FIRST”中S所在的内存单元,相当于c_3中保存了0x00000F26, 然后将c_3中的地址0x00000F26传递给printf函数,以%s格式化输出字符串”ST”
Ps:要注意 cpp指针这次并没有改变指向,仍然指向了cp[2]所在的内存空间,即cpp中保存的地址依旧是0x00000FF8
cpp[-1][-1] + 1在底层被解析成 *(*(cpp -1) - 1) + 1,这里cpp-1,cpp类型是char***,其+/-1跨越的距离是4字节,所以相当于指向了0x00000FF4所在的内存单元。即
然后解引用*(cpp -1 )就相当于拿到了cp[1],cp[1]是一个二级指针,其值是0x0000FFFC,是数组c的元素c[2]的地址,如果从指针看的话,如下所示
*(cpp -1) – 1 <==> cp[1] – 1 <==> cp_1 – 1, 这里cp_1是二级指针,其+/-1跨越的距离是一个指针所占用内存空间大小的距离,即4字节,所以,cp_1 - 1相当于cp_1指针指向了0x0000FFF8所在的内存单元,即cp[1]的地址,从指针看的话就是
而解引用之后,就相当于拿到了c[1],所以*(*(cpp -1) - 1) + 1变成了c[1] + 1,
c[1]是一个一级指针,其指向了一个常量字符串,从指针角度看的话,如下所示
c[1] + 1 <==> c_1 + 1,指针变量c_1是char*类型的指针,其+/-1跨域的距离是1字节,所以相当于c_1指向了字符串”NEW”中E所在的内存单元,相当于c_1中保存了0x00000F18, 然后将c_1中的地址0x00000F18传递给printf函数,以%s格式化输出字符串”EW”
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。