前言
本篇文章整理了一些指针的笔试题,适合初学者以及对于指针掌握并不是很牢固的朋友阅读,当然,大佬想做着玩的话可以看一看第八题~
分类:循序渐进的难度:前三题和第七题是简单题,第四题有陷阱,5、6、8比较复杂(8最难)
希望能帮助到大家更好的理解指针,话不多说我们直接开始。
1
题干
下面代码的运行结果是什么
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
答案
2,5
解析
我们采用画图的方式来一步一步的分析
*(ptr - 1)
首先,明确&a+1在内存中的位置,(&a+1)是数组指针类型
其次,将(&a+1)转换成int*类型,再存储进整形指针ptr里,
int* ptr = (int*)(&a + 1);
位置如图
然后,ptr是整形指针,所以减一,ptr向前移动一个整型,就指向5了
那么,*(ptr - 1)存储的就是5了
*(a + 1)
a此处是首元素地址,加一指向2,解引用之后,结果就是2
2
题干
假设p 的值为0x100000。 如下表表达式的值分别为多少?
已知,结构体Test类型的变量大小是20个字节
(关于结构体类型的大小,之后的文章会做详细说明)
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}* p;
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
答案
0x00100014
0x00100001
0x00100004
解析
本题考察的就是不同类型的指针加一,结果是什么
小提示:
0x1虽然是十六进制,但还是1
p是指针,指针与整数的运算,移动多少位,取决于指针变量的大小
p + 0x1
p是结构体指针,大小是20个字节,
p + 0x1,就相当于加了20个字节,所以就是0x00100014
(unsigned long)p + 0x1
p转换成了整型,已经不是指针了,而整型与整数的运算就是直接的加减
所以结果就是0x00100001
(unsigned int*)p + 0x1
p转换成了无符号整型指针,加一,跳过一个无符号整型,四个字节
结果就是0x00100004
3
题干
小端环境下,程序运行结果是什么
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
答案
4,2000000
解析
数组的存储方式如下
ptr1
(&a + 1),如图
之后,(int*)(&a + 1)将(&a + 1),转换成整型指针,再赋给ptr1
ptr1[-1]
转换一下:
ptr1[-1] == ptr+(-1)== *(ptr-1)
等于使prt1向前移动一个整型的大小,也就是4个字节
如图:蓝色箭头
所以打印结果就是0x4
ptr2
(int)a
将a强制转化成int类型,再加一,
之后再转换成整型指针,相当于较之前向后移动了一个字节
画个图方便大家理解
ptr2,就指向图中黄色的位置
*ptr2
ptr2是整型指针,解引用后能向后访问四个字节,粉色的四个字节
结果就是:0x2000000
4
题干
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
答案
1
解析
我们逐层来分析
()逗号表达式
注意,数组在初始化时没有用{},而用的是()
那么这里就是逗号表达式,我们都知道逗号表达式最后一个表达式为结果,
所以数组在初始化后,结果如下
p = a[0]
a[0]是第一行元素的数组名
并且,此时, a[0]既没有放在sizeof内,又没有取地址,
所以,这里的 a[0]指的就是第一行元素的首元素地址,就是1的地址
p[0]
p[0] == *(p+0)
所以就是1
5
题干
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
答案
FFFFFFFC,-4
解析
此处为了方便理解和美观,我们将二维数组表示成一维数组
p[4][2]
p是指针数组,指向数组,数组里有四个元素,每个元素都是整型
图解:
p[4][2]就表示为
*(*(p+4)+2)
(p+4)指向的是一个有四个元素的数组,每个元素都是int类型,
*(p+4)之后得到这个有四个元素的数组,
那么*(p+4)就相当于数组名,也就是首元素的地址
加2之后,得到下图:
p = a
p指向的数组只有4个元素,但数组名a此时指向的是第一行元素,而第一行有五个元素,
放不下,只能放四个进去,所以在运行时,会报警告:
//p的类型可以表示为int(*)[4]
//a的类型可以表示为int (*)[5]
“int (*)[4]”和“int (*)[5]”数组的下标不同
当然,程序这样也可以强行运行
图解:
a[4][2]
如图:
&p[4][2] - &a[4][2]
经过上面的解释,我们可以理解他们各自的含义
下面进行二者取出地址之后,再相减,也就是指针变量减指针变量
二者相减,得到的是他们之间的元素个数,为4
我们又知道,数据在存储时是从低地址向高地址存储的,所以, &p[4][2]更小,得到的结果是-4
%p与%d
%p
用%p的形式来打印的话,输出的是-4在内存中的存储形式,也就是补码
下面进行详细说明
//原码:
10000000000000000000000000000100
//反码:
11111111111111111111111111111011
//补码:
11111111111111111111111111111110
但是,这里解释一下%p
%p在打印时认为打印的是地址(这里可以简单地认为是无符号数),所以就直接以16进制的形式打印
11111111111111111111111111111110
提示:二进制中1111对应十六进制中的F
打印结果:
FFFFFFFC
%d
用%d的形式来打印的话,输出结果就是-4
6
题干
int main()
{
int aa[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
int* ptr1 = (int*)(&aa + 1);
int* ptr2 = (int*)(*(aa + 1));
printf("%d %d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
答案
10,5
解析
&aa + 1
取出二维数组的地址,再+1,跳过了这个二维数组,
&aa + 1是数组指针,要进行强制类型转换,转换成int*类型
*(aa + 1)
aa是数组名,表示的是二维数组的首元素,即第一行元素
(aa + 1)跳过了一行,指向的是第二行元素
解引用之后, *(aa + 1) == aa[1],也就是第二行元素的数组名,表示的是第二行首元素6的地址
*(ptr1 - 1)
ptr1是整型指针,减一之后,指向10,
解引用之后输出的结果是10
*(ptr2 - 1)
ptr2是整型指针,减一,指向5这个元素
多说一句:
ptr2在转换类型之前,就是int*类型,之后再进行类型转换可能只是为了形式美观
7
题干
#include<stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
答案
at
解析
温习旧识
字符指针
char *p = “abcdef”
该指针存储的是,该常量字符串的首元素a
a[]
a[],是字符指针数组,每个元素存储的都是字符串的首元素的地址,
如图:
a是数组名,存储的是首元素的地址,
即‘w’
pa
pa是二级指针变量,存储的是a数组首元素的地址
pa++,跳过的是pa指向元素的类型的大小,而它指向的元素类型是char*,指向的就是a数组中的第二个元素
,也就是at中a的地址
那么以字符串的形式打印*(pa),结果就是at
8
题干
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *-- * ++cpp + 3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
答案
POINT
ER
ST
EW
解析
很多朋友看到这道题可能想放弃,不要放弃,我们一步一步来分析
c[]
字符指针数组,四个元素,存储的分别是E、N、P、F的地址,
如图:
cp[]
二级字符指针数组,c是数组的数组名,即首元素的地址,
自己进行一一对应
cpp
三级指针,存储的是cp的地址,也就是cp首元素的地址
如图:
**++cpp
cpp先自增,如图
再解引用两次,第一次解引用获得c+2,第二次解引用获得字符P的地址
再以字符串的形式打印出point
*-- * ++cpp + 3
先计算++cpp
注意:此时cpp的生命周期没有结束,还在代码块之内
解引用获得c+1
再自减,变成c
此时再进行解引用,得到字符‘E’的地址,
最后+3(向后移动三位),此时指向的就是ENTER中的第二个E
最终输出结果就是ER
*cpp[-2] + 3
cpp[-2] == *(cpp+(-2))
*cpp[-2] == **(cpp+(-2))
*cpp[-2] + 3 == **(cpp - 2) + 3
(cpp - 2)指向的是c+3
两次解引用之后,字符F的地址
+3,向后移动三位,是字符S的地址
打印结果是:
ST
cpp[-1][-1] + 1
cpp[-1][-1] == *(*(cpp-1)-1)
cpp-1,指向的是c+2
如图
c+2,再减一,变成c+1
之后解引用获得字符N的地址,
加1,指向的是字符E,
最终打印的结果就是EW
结语
这8道题到这里就讲解结束了,怎么样,希望你有所收获
这类题作为笔试题,很强调自己对于画图能力的要求,这也是很重要的
我们下篇文章再见~