进阶指针(五)—— 指针笔试题

图片来源于网络

✨博客主页:小钱编程成长记
🎈博客专栏:进阶C语言
🎈相关博文:进阶C语言(一)进阶C语言(二)进阶C语言(三)进阶指针(四)

笔试题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;
}
//程序的结果是什么?
  1. &a + 1:&a是一个数组的地址,类型是int( * )[5],+1时跳过一个数组的大小。
  2. int* ptr = (int*)(&a + 1);:然后int( * )[5]类型的地址又被强转成了int*类型的地址,赋给ptr。
  3. *(a+1) 是首元素地址+1,然后再解引用。
  4. *(ptr-1) 是ptr-1,然后再解引用。
    在这里插入图片描述
  5. 所以结果是2,5在这里插入图片描述

笔试题2:

//由于还没学习结构体,这里告知结构体的大小是20个字节(在x86(32位)环境下)
#include <stdio.h>

struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p的值为0x100000(0x开头的数字是16进制的数字)。如下表达式的值分别为多少?
//已知,结构体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;
}
  1. p+0x1:p是结构体类型的指针,p+0x1跳过一个结构体的大小,结构体Test的大小是20字节,又因为一个地址对应内存中的一字节空间, 地址是以十六进制的形式打印的,所以p+0x1为0x100000+20 == 0x100000+0x16(十进制的20==十六进制的14)

  2. (unsigned long)p+0x1:结构体类型的指针p被强转为unsigned long类型的值,由地址变成了常数,+0x1和+1效果一样。

  3. (unsigned int*)p + 0x1:结构体类型的指针p被强转为unsigned int*类型的指针,+0x1跳一个过unsigned int类型的大小。

  4. %p:是以地址的 形式(地址的长度和进制类型) 打印。
    %x:以十六进制的形式打印
    %#x, "0x%x":以十六进制的形式打印,并显示出十六进制的数据标志。

    在这里插入图片描述

  5. 结果为:0x100014,0x100001,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;
}
  1. (int*)(&a+1):一个数组的地址,+1跳过一个数组的大小。最后再将int( * )[4]类型的地址强转为int*类型的地址。

  2. (int*)((int)a+1):首元素地址a被强转为int类型的数据,然后再+1。最后再将int类型的(int)a+1强转为int*类型的地址。

  3. ptr1[-1] == *(ptr1-1)
    在这里插入图片描述

在这里插入图片描述
4. 结果为: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;
}

在这里插入图片描述
逗号表达式的结果是最后一个表达式的结果。
在这里插入图片描述
2. 结果为:1
在这里插入图片描述

笔试题5:

#include <stdio.h>
int main()
{
	int a[5][5];
	int(*p)[4];//数组指针
	p = a;//类型不同,不太合适,但非要这样写也行。a-->int(*)[5],  p-->int(*)[4]
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}
  1. p = a:类型不同,不太合适,但非要这样写也行。a–>int( * )[5], p–>int( * )[4]

在这里插入图片描述2. 指针相减的差值的绝对值是指针间元素的个数。
2. %p是打印地址,认为内存中存储的-4的补码就是地址。
3. %d是以十进制的形式打印。
在这里插入图片描述

笔试题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\n", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}
  1. (int*)(&aa+1):aa是二维数组的数组名,&aa+1是跳过一个二维数组的大小。最后int ( * )[2][5]类型的(&aa+1)强转成(int * )类型。

  2. (int*)(*(aa+1))
    第一种理解方式:aa+1是二维数组的首元素地址+1,也就是第一行数组的地址+1,跳过第一行数组的大小。因为aa的类型是int ( * )[5],数组指针,所以+1后,得到的aa+1的类型也是int ( * )[5],是数组指针, 解引用时访问的空间大小是有5个int类型元素的数组的大小,得到第二行数组,也就是二维数组的第二个元素aa[1],但aa[1]又是第二行的数组名,除了两个例外,aa[1]表示第二行数组的首元素地址。最后被强转为int * 类型的地址。
    在这里插入图片描述
    第二种理解方式:因为 (aa+1) == aa[1] ,并且没有sizeof(),也没有&,所以aa[1]就是第二行数组的首元素地址==&aa[1][0]。最后再强转为int
    类型,因为第二行数组的首元素地址也是int类型的,所以强转前后类型并没有发生变化。

  3. *(ptr1-1), *(ptr2-1):因为ptr1和ptr2都是int*类型的地址,所以-1时跳过一个int类型的大小。

  4. 结果是10,5
    在这里插入图片描述

笔试题7:

//阿里笔试题
#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}
  1. char* a[] = {"work","at","alibaba"};字符串作为表达式时结果是首字符地址,所以数组中存放的都是字符串的首字符地址。

  2. char* *pa = a;:a是指针数组的首元素地址,指针数组的首元素是work的首字符地址,所以a是work的首字符地址的地址。用二级指针pa接收。

  3. pa++:因为pa存放的是char * 类型的数据的地址,所以 pa++ 时跳过一个char * 类型的大小。
    在这里插入图片描述

  4. *pa:*pa指向at,是at的首字符地址。

  5. %s:打印字符串,从这个地址开始解引用打印,遇到\0停止(\0不打印)。

  6. 结果是at
    在这里插入图片描述

笔试题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;
}
  1. char* c[] = {"ENTER","NEW","POINT","FIRST"};:数组c中放的是四个字符串的首字符地址。指针指向字符串,本质上指向的是字符串的首字符

  2. c+3:c是指针数组的首元素地址,也就是ENTER的首字符地址的地址,存放在二级指针中。所以+3是跳过3个ENTER的首字符地址的大小。

  3. char***cpp = cp;:cp是二级指针数组cp的数组名,在这里表示首元素地址,表示c+3的地址。因为c+3的类型是char**,所以要用c+3的地址要存放在三级指针中。
    在这里插入图片描述

  4. cpp前置++或–后,cpp本身也会发生改变

  5. **++cpp:前置++,先++,后使用,跳过1个char * * 类型的大小,然后两次解引用得到char*类型的指针,指向POINT。
    在这里插入图片描述

  6. *--*++cpp+3:cpp先前置++,再 * 解引用,得到char * * 类型的地址c+1。然后前置 --,再 * 解引用,得到char * 类型的地址,最后再跳过3个char类型的大小,指向ENTER中的E。
    在这里插入图片描述

  7. *cpp[-2]+3cpp[-2] == *(cpp - 2),cpp先往后退2个char * * 类型的大小,然后 * 解引用,得到char * * 类型的c+3,再解引用,得到char * 类型的地址。最后跳过3个char类型的大小,指向FIRST中的S。
    在这里插入图片描述

  8. cpp[-1][-1]+1cpp[-1][-1] == * ( * (cpp - 1) - 1),cpp先往后退1个char * * 类型的大小,再 * 解引用,得到char * * 类型的c+2。然后后退1个char * 类型的大小,在解引用,得到char*类型的地址。最后跳过1个char类型的大小,指向NEW中的E。
    在这里插入图片描述

  9. %s:打印字符串,从这个地址开始解引用打印,遇到\0停止(\0不打印)。
    在这里插入图片描述

总结

这篇文章我们通过做一些指针笔试题更加深入的理解了指针。
大家有什么问题可以在评论区多多交流,如果文章有错误的地方,欢迎在评论区指正。感谢大家的阅读!大家一起进步!

点赞收藏加关注,C语言学习不迷路!
图片来源于网络

  • 65
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 105
    评论
评论 105
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值