指针笔试题
笔试题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,当我们取地址a的时候,我取出的是整个数组的地址,我取地址a+1就指向了数组最后一个元素的后一个位置,这时候我们将&a强制类型转化为int*,因为&a的指针类型本质上应该是int(*)[5],加1之后指针类型不变,当我强制类型转化之后赋给ptr,ptr就指向了元素5后面那个位置,ptr-1向前减1就指向了元素5这个地址,解引用就得到了5,第一个%d理所当然很好理解就是2
笔试题2
#include<stdio.h>
//由于还没学习结构体,这里告知结构体的大小是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;
}
这里我们直接将0x100000这个值赋给p,同时需要将0x100000强制类型转化为结构体指针(struct Test*)
1:p+0x1—+1要跳过一个结构体(20个字节)—需要转化为十六进制数—所以为0x100014
2:当我们将p强制类型转化为uhnsigned long的时候,就回归到了我们普通的数值的计算上来,也就是0x100000+1—>0x1000001
3:p强制类型转化为unsigned int*的时候,这里的+1要跳过字节,所以应该为0x100004
后期的结构体大小需要自己去计算
笔试题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;
}
这里设计大小端的知识
我们当前的机器是小端,所以按照小端存储的方式来存储
此时的&a在01的位置
&a+1跳过整个数组就指向了00的后一部分
所以ptr1也就指向了00的后一部分
所以ptr1[-1]==(ptr1-1)
ptr1[-1]向前跳了一个整型也就走到了前面的00的最后一个位置
接下来看ptr2//
a是首元素的地址,a的指针类型本身应该是Int的,我将他强制类型转化为整型,整型加1就得到了下一个字节,这个可以类比笔试题2的知识,有异曲同之妙
所以此时ptr2就指向了01后面的00的位置了
所以这个时候我要从00这个位置开始向后面访问4个字节所以打印出来时2000000,大家可以去验证一下,具体流程图见下图
笔试题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,3,5三个数组依次排序,其他位补0即可
所以我们得到1这个答案
这里的p不是野指针,因为我在定义了这个指针类型之后立马给大传了低第一行数组名首元素的地址给他
笔试题5
#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;
}
后面会有图片演示,现在我给大家文字叙述
数组名的类型为int()[5],p的类型为int()[4],将数组名赋给p
p最终指向的是a的首元素的地址,p向后走一步是要跳过4个整型的
看图
随着数组下标的增长,地址是由低到高变化的
指针减去指针得到的是指针与指针之间的元素个数
所以我们%d打印的是-4,-4在内存中是以补码的形式存储的
那么%p怎么处理呢?
//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]);//-4
// //10000000000000000000000000000100
// //11111111111111111111111111111011
// //1111 1111 1111 1111 1111 1111 1111 1100
// //F F F F F F F C
// //0xFFFFFFFC
//
// return 0;
//}
//
%d是以原码的形式打印有符号的整数
内存中存储的是补码
-4以%p打印的时候不存在有无符号之分
他就将1111 1111 1111 1111 1111 1111 1111 1100当作十六进制的地址打印就可以了,老铁们可以拷贝代码去进行验证
笔试题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的指针类型为int(*)[5]
ptr1指向的是元素10后一个位置
所以需要强制类型转化为int*
ptr1-1就得到了元素10处的地址,再解引用就得到10这个元素
对于ptr2指向了第二行数组的首元素地址
数组名相当于首元素的地址
所以这里得到的是元素6处的地址
所以ptr2-1再解引用就得到地址5处的元素值5
笔试题7
#include<stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}
这是一个字符指针,a数组名首元素的地址,将一级指针首元素的地址用一个二级指针来接收。pa就指向work这个元素
pa++就指向at这个元素的地址,然后解引用就得到了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;
}
我们对比着下图来讲
cpp存放的是cp首元素的地址,cpp先前置++,当我在前置++完过后我就指向了C+2的位置了,执行完前置++之后就要执行*号了//
第一个星号先解引用得到了cp里面第二个元素地址的值,而C+2是指向"POINT"处的地址,此时我再次解引用操作就得到了”POINT“这个元素
我们此时有再一次讲CPP++了一次,这是CPP就指向了C+1,先解引用得到C+1这个值,然后再次–就得到了C这个值,C指向的是"ENTER"这个元素,然后加3向后移三位得到ER,%s打印到\0结束,所以我打印出来的是ER
值得注意的是这里的CPP是随时在发生变化的
我们接着走
CPP[-2]=CPP-2在解引用就得到了CP+2这个位置的值,CP+2指向的又是”FIRST“这个元素的地址,我再次进行一次解引用就得到了"FIRST"这个值,然后首元素加3就得到了S这个元素,再次%s打印字符串到\0结束,所以打印出来ST
最后一个
想象成一个二维数组最后一个打印可以写成下面代码
*(*(cpp - 1) - 1) + 1;
同理,C+1指向元素”NEW“,元素N加1得到E,%s打印到\0结束就得到了EW
这道题的图解,图有点乱,但文字叙述给大家叙述清楚了的