这里写目录标题
- 指针运算笔试题解析
- 题目1
- 解析
- 结果
- 题目2
- 解析
- 结果
- 题目3
- 解析
- 结果
- 题目4
- 解析
- 结果
- 题目5
- 解析
- 结果
- 题目6
- 解析
- 结果
- 题目7
- 解析
- 结果
- 题目8
- 解析
- 结果
感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒个人主页
🥸🥸🥸C语言
🐿️🐿️🐿️C语言例题
🐣🐓🏀python
指针运算笔试题解析
题目1
#include <stdio.h>int main(){int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;}//程序输出的结果是什么
解析
a[5]是一个整形类型的数组,&a是取的整个数组的地址,因此&a+1就是跳过整个数组,(int*)是强制类型转换,是将&a+1强制转换成int类型的指针(&a+1也可以写成int(*)[5])
*(a+1)中的a是数组首元素的地址,a+1=&a[1],解引用结果就是a[1]=2
ptr-1这里就要好好说一下了,我们用一个图来表示
如图因为&a+1是跳过整个数组,因此ptr的地址在a[4]之后,与a[4]相差4个字节,而ptr-1是跳过1个元素,也就是往前跳过4个字节,因此ptr-1的地址其实就是&a[4],解引用之后就是a[4]=5
结果
题目2
//在X86环境下,假设结构体的大小是20个字节,程序输出的结构是什么
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}
解析
0x开头的是16进制,因此0x1其实就是1,p是结构体指针,因为结构体指针+1是跳过一个结构体的大小,而结构体大小是20个字节,又因为是16进制,所以结果是0x100014
,(unsigned long)p是将p强制类型转换为无符号的long,所以p+1就是0x100001
而printf(“%p\n”, (unsigned int*)p + 0x1),因为是将p转换为无符号整形指针,所以+1就是跳过4个字节,就是0x100004
因为是16进制的地址,所以需要4个字节,结果是
0x00100014
0x00100001
0x00100004
结果
题目3
#include <stdio.h>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;}
解析
int* ptr1 = (int*)(&a + 1)和第一题是一样的所以就不解释了
int* ptr2 = (int*)((int)a + 1)中(int)a是将a强制转换为整形类型,因为整形类型的运算和数学运算是差不多的,因此这里的+1其实就数学上的+1,举个例子假如a的地址是0x0012ff40,通过转换这个地址对应的数字是1244992,所以(int)a+1其实就是1244992+1=1244993,然后将这个结果强制类型转换为整形指针,所以转换之后的地址就是0x0012ff41
我们知道内存存储是分大小端的,这里VS是用小端存储
当用%x打印是02 00 00 00中2前面的0不会打印,所以结果是2000000
而*ptr打印就是4
结果
题目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;}
解析
这是一个二维数组,但是要注意数组的元素,我们可以看到数组中用括号括起来了两个数组,这里需要用到逗号表达式, (,)其实只有最后一位有效,(0,1)就是1,因此二维数组我们可以这样写,a[3][2]={1,3,5};因为是三行二列,而{ }中只有三个元素,剩下的既全为0,a[0]在上一篇博客已经讲了,是一维数组{1,3}的数组名,因为p=a[0],所以p[0]=a[0][0],最后结果就应该是1
结果
题目5
假设环境是x86环境,程序输出的结果是什么
#include <stdio.h>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;}
解析
(p)[4]是一个数组指针,因为p=a中a是数组名表示数组首元素的地址,所以a其实就是int( * )[5],而p是int()[4]
因为p中只有4个元素,所以p+1就是跳过4个整形元素,因此绿色是p+4所包含的元素,红色就是a[4][2],对于&p[4][2] - &a[4][2]就是-4,用%p来打印的话就需要将-4的补码写出来
-4的原码:10000000 00000000 00000000 00000100
反码: 111111111 111111111 111111111 111111011
补码: 111111111 111111111 111111111 111111100
因为是打印的%p是地址,所以将补码看成16进制来打印,结果是FFFFFFFC
而用%d打印的话其实就是-4
结果
题目6
#include <stdio.h>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;}
解析
&aa+1中&aa是整个二维数组的地址,因此&aa+1是跳过整个二维数组,(int*)将&aa+1强制转换为整形类型的指针
(aa+1)中aa是第一行的地址,(aa+1)就是aa[1],aa[1]就行相当于第二行的数组名,aa[1]=&aa[1][0],这里的强制类型转换其实是多余的,因为第二行第一个元素本来就是整形,取他的地址就是整形类型的指针
所以*(ptr1-1)就是10,*(ptr2-1)就是5
结果
题目7
#include <stdio.h>int main(){char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;}
解析
这是一道阿里巴巴的笔试题
chara[]是一个数组,数组里面的类型都是char,数组括号里面都是三个字符串,但是这三个字符串是放不下,因为这个数组里面的都指针char*,字符串是放不进去的,所以放进去的是他们的地址,比如work存放在a中的地址是w的地址,at存放的则是a地址…
而pa存放的是a的首元素地址,pa++后就是a的第二个元素如图
结果
题目8
#include <stdio.h>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;}
解析
上面是没有进行自增自减操作时每个数组元素对应的地址
char*c中存储的分别是
ENTER中E的地址
NEW中N的地址
POINT中P的地址
FIRST中F的地址
而cp中c+3就是第四个元素的地址,c+2就是第三个元素的地址…
cpp是将cp的首元素地址传给cpp,所以cpp就是c+3的地址
上图是经过自增自减操做后的结果,
printf(“%s\n”, **++cpp);对应的颜色是红
printf(“%s\n”, *-- * ++cpp + 3);对应的颜色是绿
printf(“%s\n”, *cpp[-2] + 3);对应的颜色是黑
printf(“%s\n”, cpp[-1][-1] + 1);对应的颜色是粉
我们一个一个的解析
第一次打印: **++cpp先将cpp进行自增,所以cpp中的cp从cp中第一个元素c+3变到第二个元素c+2 然后两次解引用之后就是POINT中P的地址,打印字符串就为POINT
第二次打印:由于++cpp将cpp中cp指向的地址永久变化了,所以第二次的++cpp就是从c+2变到了c+1,解引用再–,c+1就变成了c,所以c+1指向的地址就由1变成0,之后再解引用就是ENTER中E的地址,再+3后,就是箭头值的第二个E的地址,打印字符串就是ER
第三次打印:*cpp[-2]+3我们可以写成 * *(cpp-2)+3,由于上一次打印使cpp指向的c+2变成了c+1,所以这里的cpp-2就使cpp指向的第三个元素c+1变为第一个元素c+3(注意这里不是自增自减),再经过两次解引用后,就是FIRST中F的地址,+3就变成了箭头指向的S的地址,所以打印结果为ST
第四次打印cpp[-1][-1] + 1可以写成 *( *(cpp-1)-1)+1,cpp-1就是从第三个元素c+1变为第二个元素c+2,解引用再-1就是从原来指向的下标2变成指向的下标1,解引用再+1就是NEW中E的地址,打印的结果就是EW