• 指针和数组笔试题深度解析(下)


    前言

    上期我们介绍了strlen、sizeof的各种用法,本期带大家来学习指针类的笔试题。大家也要多多思考才行。

    指针笔试题

    首先我们来了解两点:

    %p是打印地址
    %x是以16进制形式打印

    题一

    #include
    int main()
    {                 //程序的结果是什么?
    	int a[5] = { 1, 2, 3, 4, 5 };
    	int *prt = (int *)(&a + 1);
    	printf( "%d,%d", *(a + 1), *(prt - 1));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出:2,5
    分析:
    我们知道 &a+1 里 &a 取出的是整个数组地址(&a的类型是 int(*)[5] ,该类型大小为5个字节长度) +1 是跳过整个数组,指向的位置如图:
    在这里插入图片描述
    前面加个 (int *) 是强制类型转换的意思,将 int( * )[5] 类型强制转换为 int 类型,那么该类型的变量加一减一所跳过长度就是4字节(上期有讲过指针变量加一减一步长)
    因此,prt - 1 所指向的位置如下图:
    在这里插入图片描述
    解引用就得到 5 ,
    (a + 1)==>a[1] ,也就是2.

    题二

    我们来看一道关于结构体指针的例题:

    #include
    struct Test
    {
    	int Num;
    	char* pcName;
    	short sDate;
    	char cha[2];
    	short sBa[4];
    }*p;
    //假设 p 的值为0x100000。 如下表表达式的值分别为多少?
    //已知,结构体Test类型的变量大小是20个字节
    int main()
    {
    	p = (struct Test*)0x100000;    //因为0x100000的类型是int型,所以要强制类型转换
    	printf("%p\n", p + 0x1);
    	printf("%p\n", (unsigned long)p + 0x1);
    	printf("%p\n", (unsigned int*)p + 0x1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    本题考查指针加一步长多少?
    输出:
    在这里插入图片描述
    分析:

    	p = (struct Test*)0x100000;    
    	printf("%p\n", p + 0x1);    //00100014    
    	//指针 p 所指向的类型是 struct Test ,该类型占20字节单位,因此加一跳过20字节。
    	printf("%p\n", (unsigned long)p + 0x1);   //00100001
    	//强制类型转换为 unsigned long 类型,就是一个整数,整数加一就是加一个字节
    	printf("%p\n", (unsigned int*)p + 0x1);   //00100004
    	//强制类型转换为 unsigned int* 类型,p就指向一个无符号整型,占4个字节,加一就是加4个字节
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    题三

    #include
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:4,2000000
    分析:

    	int a[4] = { 1, 2, 3, 4 };
    	int* ptr1 = (int*)(&a + 1);      //&a取出整个数组地址,加一跳过整个数组,强制类型转换为int *型,prt1指针指向下图1位置
    	int* ptr2 = (int*)((int)a + 1);  //将首元素地址强制类型转换为整型,加一跳过一个字节,再转换回来,int*类型解引用访问4个字节,见图二
    	printf("%x,%x", ptr1[-1], *ptr2);   //prt1[-1]==>*(prt1-1),prt1-1指向下图1位置
    
    • 1
    • 2
    • 3
    • 4

    图一:
    在这里插入图片描述
    图二:
    在这里插入图片描述

    题四

    #include 
    int main()
    {
    	int a[3][2] = { (0, 1), (2, 3), (4, 5) };   数组的初始化内容有逗号表达式,实际上数组初始化的是1,3,5
    	//如果要将上数全部存进去,可以这样写{{0,1},{2,3},{4,5}},或者{0,1,2,3,4,5}
    	int* p;
    	p = a[0];
    	printf("%d", p[0]);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:1
    解析:

    	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    	int* p;
    	p = a[0];    //将a[0][0]存放入p中
    	printf("%d", p[0]);
    
    • 1
    • 2
    • 3
    • 4

    题五

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:
    在这里插入图片描述
    解析:
    p的首地址指向a的首地址,我们将数组a画出来,指针p指向的数组是4个元素,如下:在这里插入图片描述
    所以,p[4][2]和a[4][2]如图:
    在这里插入图片描述
    &p[4][2] - &a[4][2],指针相减结果就是两地址间的元素个数,就是4,但是它是小的减大的所以是-4,以%p打印就是FFFFFFFC,以%d打印就是-4

    题六

    #include 
    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));   //aa代表首元素地址即aa[0]的地址,+1就是a[1]的地址
    	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:10,5
    不多解释了

    题七

    #include
    int main()
    {
    	char* a[] = { "work","at","alibaba" };
    	char** pa = a;
    	pa++;
    	printf("%s\n", *pa);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:at
    分析:a[0]存放‘w’的地址,a[1]存放‘a’的地址,a[2]存放‘a’的地址,如图(两个a地址不同,由首字符地址可以找到该字符串)
    在这里插入图片描述
    pa++,原来pa指向a[0],现在指向a[1],解引用得到a[1]的内容即地址,打印字符串得到 at

    题八(稍难)

    注意题中的++、- - 哦!

    #include 
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    输出:
    在这里插入图片描述
    相信大家应该都有所疑惑吧,下面我们来分析一下:

    	char* c[] = { "ENTER","NEW","POINT","FIRST" };
    
    • 1

    由该代码可以画出下图:
    在这里插入图片描述

    	char** cp[] = { c + 3,c + 2,c + 1,c };
    	char*** cpp = cp;
    
    • 1
    • 2

    由此又可以得到下图:
    在这里插入图片描述

    	printf("%s\n", **++cpp);
    
    • 1

    cpp先++,再解引用,++就改变了cpp里存放的地址,使原本指向cp[0]的指针++变为指向cp[1],如图:
    在这里插入图片描述
    解引用得到c[2]的地址,解引用得到字符‘P’的地址,打印就是:POINT

    	printf("%s\n", *-- * ++cpp + 3);
    
    • 1

    cpp在原来基础上再++,如图:
    在这里插入图片描述
    cpp解引用得到cp[2],–cp[2]就是将cp[2]里的地址–,所以cp[2]里地址指向了c[0],如图。解引用得到的地址指向字符‘E’,加3个字节就指向了字符E,打印字符串为ER
    在这里插入图片描述

    	printf("%s\n", *cpp[-2] + 3);
    
    • 1

    经由几次++后,cpp指向cp[2] ,cpp[-2]可以写出*(cpp-2) ,得到cp[0],也就是c[3]的地址,再解引用得到字符‘F’的地址,加3得到‘S’的地址,打印字符串得到 ST

    	printf("%s\n", cpp[-1][-1] + 1);
    
    • 1

    cpp[-1][-1] 可以写成*(*(cpp-1)-1) ,cpp-1得到cp[1]的地址,解引用得到c[2]的地址,再-1得到c[1]的地址,解引用得到字符‘N’的地址,加一字节,得到字符‘E’的地址,打印字符串,得到EW

    好了关于指针的系列就告一段落了,相信大家应该收获满满吧,下期见了~

  • 相关阅读:
    【音视频基础】音频基础理论
    3.2 Keepalived安装部署
    释放计算潜力:SKlearn模型并行训练指南
    HTML基础学习第五篇(HTML表单与输入)
    RocketMQ(五)RocketMQ集群架构
    Android获取本地文件目录
    P5906 【模板】回滚莫队&不删除莫队
    消息队列-Kafka-消费方如何分区与分区重平衡
    R语言Sys.Date函数获取当前日期、抽取日期数据中的年、月、日信息、日期在周内第几天、年内第多少天
    一步一步把废旧的电脑变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(一)
  • 原文地址:https://blog.csdn.net/qq_71360748/article/details/126820003