作者:旧梦拾遗186
专栏:C语言编程----小比特成长日记
每日励志:
生活总是遍体鳞伤,可是后来,那些受伤的地方一定会变成我们最强壮的地方——海明威
前言:
为了巩固我们所学的指针知识,今天小编带大家做几道,大厂的笔试题。
目录
前言:
为了帮助小伙伴们更好的降服指针,小编特地为小伙伴们总结了八道指针的笔试题目,并有本人的详细解析,希望同学们仔细阅读,如有不懂的可以私信小编哦。
题目:
笔试题 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
答案:2 5
解析:
在本题中a是数组名代表数组首元素的地址,此时可将a看为指针,a+1,代表指针后移一位,此时再对a+1解引用*(a+1),得到第二个元素的值,所以*(a+1)的答案为2。
&a代表整个数组的地址,故在进行(&a+1)的操作中跳过了整个数组,而&a带表的是整个数组的地址其指针类型为int(*)[5](数组指针),而ptr的指针类型为int*(整型指针),所以此时在进行赋值操作时,需要将(&a+1),强制转换为整型指针(若不转化编译器会报警告,在编译时还是会以ptr的类型为准),故ptr-1代表整型指针向前移动以为再进行解引用*(ptr-1),得到结果为5。细则如图。
笔试题 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; }
答案: 0x100014 0x100001 0x100004
解析:
可以先给p进行赋值p=(struct Test*)0x100000
在本题中0x代表16进制,而0x1代表1,由于p是指针类型,指针类型+1代表跳过此类型的大小既:
1)一个整形指针+1代表跳过一个整形的大小
2)一个结构体指针+1代表跳过一个结构体的大小
3)一个字符指针+1代表跳过一个字符的大小
故:p+1代表跳过一个结构体类型大小(20字节),化简为16进制为14再加上0x100000结果为0x00014;
(unsigned long)p代表普通整型类型,故此时的运算部位指针运算而为普通的整数运算结果为0x100001;
(unsigned int*)p把结构体指针类型强制转化为整型指针类型,此时为指针运算挑过的是一个整型类型的大小结果为0x100004;
笔试题 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 20000000
解析:
&a代表整个数组的地址,故在进行(&a+1)的操作中跳过了整个数组,而&a带表的是整个数组的地址其指针类型为int(*)[5](数组指针),而ptr的指针类型为int*(整型指针),所以此时在进行赋值操作时,需要将(&a+1),强制转换为整型指针(若不转化编译器会报警告,在编译时还是会以ptr的类型为准),又因为ptr1[-1]可转化为*(ptr1-1),故可看为ptr1-1代表整型指针向前移动以为再进行解引用*(ptr-1),得到结果为4。细则如图1。
因为a为数组名为首元素地址,可假设为0x0012ff40,而(int)a把a转化为整型类型,此时的a可表示为0x0012ff40,之后再进行+1,可表示为0x0012ff41,再强制转化为(int*)赋值给ptr2,相当于指针向后移动了一个字节(细节如图2),由于采取的是小端存储,再进行转化时需要从高地址向低地址读取,最后打印结果为20000000
图1:
图2:
笔试题 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
解析:
做这道题的时候千万不要掉到坑里,数组这样初始化int a[3][2] = { (0, 1), (2, 3), (4, 5) };和这样初始化int a[3][2] = { {0, 1}, {2, 3}, {4, 5} };是不同的,前者为逗号运算,逗号运算(从左向右一次运算,取最右边的值),所以此时数组被初始化为int a[3][2] = { 1,3,5};如图1。
a[0],代表数组第一行首元素的地址即为&a[0][0],而p[0],可表示为*(p+0),所以p[0]=&a[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;
}
答案:ff ff ff fc -4
解析:
由题我们可以知道p的的类型为数组指针即为int(*)[4],而a为数组首元素地址又因为a[5][5],为二维数组,则a代表的是第一行的地址相当于&a[0],很明显两者的类型不同,故赋值时会报错,但是仍可以运行,通过绘图我们可以找到a[4][2]和p[4][2]的具体位置,而地址(指针)减地址(指针),求出来的是地址之间元素的个数,故%d打印出来的结果为4.(如图1)
当以%p打印时我们可知-4的原反补
原码:1000 0000 0000 0000 0000 0000 0000 0100
反码: 1111 1111 1111 1111 1111 1111 1111 1011
补码: 1111 1111 1111 1111 1111 1111 1111 1100
可化为ff ff ff fc
图1:
笔试题 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
解析:本题和前集体类似,在画图的时候注意化成一行即可;
笔试题 7:
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
答案:at
解析:
char *a[]说明此为指针数组,里面存储的类型为char*,所以此时需要用二级指针来存储一级指针的地址,char**pa可以这样理解,char*(说明了pa指向了char*类型)*(说明了pa为指针类型)pa。
所以本题中pa++跳过得是一个char*类型的大小指向at所以结果为at
%s打印只需要一个地址即可
笔试题 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
解析:
首先我们要用清楚指针间的关系图如下:
a)当我们执行++cpp,则cpp指向了cp[1],故此时第一次解引用找到了c[3]的地址,再解引用找到了“POINT”的首地址,进行打印结果为POINT。
b)第二个打印值时我们先进行++cpp运算指针指向cp[2]的地址再解引用得到c[1]的地址再--得到c[0]的地址此时解引用得到“ENTER”的首地址,在+3得到E的地址此时打印的结果为ER
c) 当执行第三个打印值时,可变化为
*cpp[-2]+3-->*(*(cpp-2))+3
即cpp-2为cp[0]的地址解引用得到cp[0]的地址,再解引用得到c[3]里的内容即为F的地址,为“FIRST”的首元素地址+3为s的地址故结果为ST
d) 当执行最后一个时和上边的类似得到ER
cpp[-1][-1]+1-->*(*(cpp-1)-1)+1
总结
以上八到题如果能够熟练掌握,说明了我们已经对指针有了深入的了解,也知道了画图时做出指针难题的最大利器,希望小伙伴们真正看懂这些题。
如有收获希望三连哦嘿嘿嘿