??🌈hello! 各位宝子们大家好啊,指针系列的讲解我们就全部讲解完毕了,那么接下来就是来检验成果的时候了?
????今天来做一做指针的面试题到底是个什么难度!你真的学会了指针了嘛?这篇文章让你融汇贯通
??📚本期文章收录在《C语言进阶篇》,大家有兴趣可以看看呐!
???? 欢迎铁汁们 ?? 点赞 👍 收藏 ?留言 📝!
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
//程序的结果是什么?
printf( “%d,%d”, *(a + 1), *(ptr - 1));
其实没什么难的,我相信各位铁子们已经想出答案了!关于数组的笔试题我们做了那么多,&数组名就是拿出整个数组的数组名而+1就是跳过整个数组+1;
所以 *(ptr-1)就是回到了数组的最后一个元素的位置
然后进行解引用 结果就是:5
*(a + 1) a 是首元素的地址,所以+1 就是指向第二个元素的地址
然后进行解引用 结果就是:2
📑图片展示:
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
🔥 注:本代码是在32位环境下测试的。
printf(“%p\n”, p + 0x1);
这里就非常有意思了指针加一我们都知道是跳过指针类型的大小,整形指针跳过4个字节,字符指针跳过1个指针。而我们这里结构体的大小是20个字节,所以+1跳过的就是20个字节!
20
的16进制就是 14
printf(“%p\n”, (unsigned long)p + 0x1);
而这里则强制转换成了整形,而强转成整形的话。那么 p 就是一个整数了。而整数+1就是 +1 没有什么变化!
printf(“%p\n”, (unsigned int*)p + 0x1);
这里指针 p 被强制转换为了无符号整形,而无符号整形+1。就是跳过4个字节。
🌴 总结:
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);
我们先来看一下,&a先把整个数组的地址拿出来然后进行+1然后强制转换为 int 类型的指针传给我们 ptr1。*
int *ptr2 = (int *)((int)a + 1);
🔥 注:这里需要用到 大小端存储的概念!不会的也去看看 这里只简单讲解哦!
这里就非常有意思了,a是数组首元素的地址。这里却把它强制转换为了整数,然后+1是在括号里面所以a+1,在转换为int* 类型的地址就只跳过了一个字节!
然后再进行+1 跳过4个字节,进行解引用访问4个字节
所以int *ptr2 = (int *)((int)a + 1);
🔥 *ptr2 的结果是 0x02000000
这里给大家解释一下大小端存储,就地址存低位,高地址存高位。所以拿出来的时候低地址就是低位,高地址当高位还原的。
#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;
}
int a[3][2] = { (0, 1), (2, 3), (4, 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;
}
这个题也是一个陷阱,我们首先要搞懂,数组的地址变化是由数组下标由低到高变化的而且数组还是一块连续的储存空间。
a
是 int (*)[5] 指针 p
是 int (*)[4] 所以会发生隐式转换。到int (*)[4]这里我们就可以看到p[4][2]a[4][2]之间相差了,4个整形的长度。
但是如果以地址打印的话得到的就是,-4 在内存中存储的数字。而 -4 在内存中是以补码的形式存储的。
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 就是跳过整个数组指向数组后一个元素。
而 *ptr2 = (int *)(*(aa + 1));这里 aa 代表的就不是首元素的地址了,而是一维数组 aa[0] 整个一维数组的地址,所以+1 就跳到了 aa[1].
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
这里要注意也是一个陷阱, char *a[] = {“work”,“at”,“alibaba”}; a[ ]
里面可以当成存放的是一维数组字符串首元素的地址。所以当我们把 字符指针 数组 *a[]
存放到 二级指针 pa 里面的时候他们的内存布局就应该是这样的!
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;
}
做这种题首先把图画出来就简单多了
printf(“%s\n”, **++cpp);
**++cpp指向的就是 cp[1] , 向前了一小步。这时看这我们上面画的图就简单多了!而这时又进行了俩次解引用不就找到 c[2]嘛!
printf(“%s\n”, *--*++cpp+3);
这个首先要把运算符的逻辑关系搞清楚,做这个题就简单多了 ++ -- 的运算符关系是比 + 号运算优先级要高的。
printf(“%s\n”, *--*++cpp+3);
所以结果为 ER
printf(“%s\n”, *cpp[-2]+3);
这里我们和上面一样, *cpp[-2]+3 = *(cpp-2)+3 , 我们把它简化一下就简单多了!
cpp-2
然后再进行解引用指向的就是这里 printf(“%s\n”, cpp[-1][-1]+1);
诶这里大家千万不要进入误区了,使用下表引用是不会改变 指针的指向的,所以我们cpp 指向的还是 cp[2]。
cpp[-1][-1]+1 = ((cpp-1)-1)+1
先 -1 然后再进行解引用,就找到了这里
然后再进行 +1 打印出来就是从这个位置开始的!
printf(“%s\n”, cpp[-1][-1]+1);
**结果为 EW **
? 归纳:
好了以上就是关于经典的八个经典笔试题的详细解析,包含了个个方面的知识不知道大家做的怎么样呢!
??指针 - 指针的运算
??操作符的优先级
??++ 对指针的影响
??[] *() 的理解
??指针的运算
?? 这些就是全部指针 and 数组的全部知识的总结了,不知道大家掌握的怎么样呢?
看到这里了还不给博主扣个:
?? 点赞
??收藏
?? 关注
!
💛 💙 💜 ?? 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。