八道指针笔试题

发布于:2022-12-17 ⋅ 阅读:(409) ⋅ 点赞:(0)

在这里插入图片描述



一、笔试题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;
}
//程序的结果是什么?

  首先定义了一个数组a,其有5个元素每个元素为int型。接着&a(取出的是整个数组a的地址,因为&数组名取出的是整个数组的地址),然后+1(跳过一整个数组,也就是数组a,地址指向数组末尾的位置;原因是:地址(指针)的类型的决定了加减整数时的步长),最后将这个地址强制类型转化为int*,再赋值给int* ptr。内存布局如下图所示:

在这里插入图片描述

  所以*(a + 1)就是在访问数组a第二个元素,也就是这里的2;*(ptr - 1)就是在访问数组a第五个元素,也就是这里的5(因为地址(指针)的类型决定了其在解引用操作的时候能过访问空间的大小,即:int型指针解引用访问int型大小的空间,char型指针解引用访问char型大小的空间)。故printf打印的及如果为2,5

在这里插入图片描述


二、笔试题2:

//已知,结构体Test的类型大小是20个字节,假设p 的值为0x100000。
//问:如下表表达式的值分别为多少?
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;
}

  首先创建了一个结构体类型Test,然后用该结构体类型定义了的指针变量p,并初始化为0x100000(也就是使指针p指向了地址为0x100000的内存空间)。接着调用主函数:

  第一个printf:打印的是p + 0x1的地址,也就是p+1的地址(因为这里的0x1表示的是十六进制的1,转化为十进制也是1)。由于p是一个指向Test类型结构体的指针,所以+1就是跳过一个该类型结构体的大小,也就是20个字节,故打印结果为0x100000 + 20 = 0x100014

  第二个printf:打印的是(unsigned long)p + 0x1,运算顺序是先将指针强制类型转化为unsigned long类型,也就是无符号长整形,那对整型+1不就仅仅只是加一嘛。故表达式(unsigned long)p + 0x1的结果为0x100001,然后printf以地址类型打印整型类型的0x100001,结果为:0x100001

  第三个printf:打印的是(unsigned int*)p + 0x1,与之前一样先强制类型转化unsigned int*注意:这可是一个指针类型啊,加减整数可不仅仅只是加减整数),所以这里的p+1其实是跳过了一个int型的大小,也就是4个字节。故打印结果为:0x100000 + 4 = 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;
}
//问:打印结果是什么?

  首先创建了一个int [4]型的数组,然后为int*类型的指针ptr1和ptr2赋值,其中int *ptr1 = (int *)(&a + 1);与笔试1类似,指向的是数组a的末尾。这道题主要考点这于(int *)((int)a + 1),它先是将a强制类型转化为int型,也就是将数组a首元素的地址强制类型转化为int型,当成整型来看(因为数组名一般情况下表示数组首元素的地址)。然后对其+1,自然结果仅仅就是加一,然后将得到的这个整型数强制类型转化为(int*)后赋值给pt2,故此时ptr2指向的就是数组首元素地址向后跳一个字节后的空间。内存布局如下图所示:

在这里插入图片描述

  最后打印ptr1[-1]等价于*(ptr-1),printf是以%x(十六进制)的形式打印,所以最后的结果为:4,2000000

在这里插入图片描述


四、笔试题4:

int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
//问:最后打印结果是什么?

  首先定义了一个3行2列的二维数组,然后对其初始化{ (0, 1), (2, 3), (4, 5) },大家有没有觉得很变扭,初始化二维数组什么时候用()啦?不因该是{ {0, 1}, {2, 3}, {4, 5} }这样嘛。所以这里的()并不是代表一行,而因该是逗号表达式,故这里的初始化因该是不完全初始化,内存布局如下图所示:
在这里插入图片描述
  接着将p = a[0];,其中a[0]表示的数组a第一行的数组名,而数组名又表示数组首元素的地址,故这里的a[0]表示第一行首元素的地址,即a[0][0]的地址。然后printf打印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;
}
//问:最后打印结果是什么?

  首先创建了一个5行5列的二维数组a,然后又创建了一个数组指针p(其指向的数组有4个元素,每个元素都是int型的)。我们有知道二维数组的数组名本质上表示的是第一行的地址,p = a;就是将二维数组a第一行的地址赋值给了数组指针p(注意:虽然它两的类型不同,但传递的值是不变的),故你会发现此时a和p指向了同一个位置。

  其实我们完全可以把p和a都看成二维数组(只不过这两个二维数组的起始位置是相同的,而且p代表的二维数组每行只有4个元素,而a代表的每行却有5个元素)。然后来理解 &p[4][2] - &a[4][2],这求的不就是两个地址之间元素的个数嘛(因为指针 ‘-’ 指针的值是,指针之间元素的个数),而且是二维数组p第4行第2列元素地址与二维数组a第4行第2列元素地址之间元素的个数,那不就只要知道p[4][2]前元素的个数和a[4][2]前元素的个数之后相减就可以得到想要的结果了(注意:指针‘-’指针的值也可能会是负数)。p[4][2]前元素的个数是4 * 4 + 2 = 18a[4][2]前元素的个数是4 * 5 + 2 = 22,故&p[4][2] - &a[4][2]的结果为 -4 。内存布局如下图所示:

在这里插入图片描述

  然而这道题还没有做完,他还考了一个知识点,printf是要以两种不同的形式分别输出&p[4][2] - &a[4][2]表达式的结果。我们知道%d表示是以有符号整型输出,故输出因该是:-4。而%p是专门用来打印地址的(而且是十六进制形式输出),但我们要知道地址在存放的时候可没有原码、反码、补码这么一说,它是直接放入内存当中的,故取的时候也直接取就完事了,不需要经过任何的转换(即内存当中是什么,打印的就是什么)。打印结果就是-4的补码,即:ff ff ff fc。

在这里插入图片描述


六、笔试题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;
}
//问:最后打印结果是什么?

  首先创建了一个2行5列的二维数组aa,然后你会发现这题的考点其实与之例题3的考点差不多。只不过是将一维数组的数组名换成了二维数组的数组名罢了。下面看内存布局:

在这里插入图片描述
在这里插入图片描述


七、笔试题7:

int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}
//问:最后打印结果是什么?

  首先创建了一个字符指针数组,然后使每一个字符指针都指向一个常量字符串,接着创建了一个二级指针pa,让其指向指针数组a首元素a[0]。内存布局如下图所示:

在这里插入图片描述
  *pa访问的是a[1],而a[1]中存放的是字符串“at”首字符a的地址,所以printf打印的结果是“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;
}
//问:最后打印结果是什么?

  关于这道题大家就不要吝啬你的草稿纸了,内存布局画出来分析吧,不然光靠想是做不出来的,因为太过于混乱了。内存布局如下图所示:

在这里插入图片描述
  接下来就是简单的根据表达式的优先级的运算,来最终求出打印的结果。

在这里插入图片描述


总结

  我们在看代码的时候可不能只是看到代码本身啊,一定要做到看代码就能联想出其对应的内存布局,如果能做到这一步那我们不管面对什么题目都可以做到一目了然了。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到