【C语言】深度理解指针(下)

一. 前言💎

昨晚整理博客时突然发现指针还少了一篇没写,今天就顺便来补一补。上回书说到,emmm忘记了,没事,我们直接进入本期的内容:

本期我们带来了几道指针相关笔试题的解析,还算是相对比较轻松的。话不多说,让我们来看看吧 👀

哦对了,如果对前两期有兴趣的话可以点击以下链接进行跳转:

【C语言】深度理解指针(上)
【C语言】深度理解指针(中)

二. 经典笔试题详解✨

1. 笔试题NO.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
  • 首先,在*(a+1)中,数组名a单独出现,代表首元素地址,则a+1为第二个元素地址,解引用后就是2。这相比大家都没有问题。

  • 关键在于第二个,&a是取出的是整个数组的地址,+1后指针就越过数组指向下一个位置。但是由于强转为整形指针赋给了ptr,因此ptr-1其实是移动了一个整形4个字节的大小而不是移动了整个数组的大小,因此(ptr-1)就回到了最后一个元素,解引用后即为5。

2. 笔试题NO.2

//程序的结果是什么?
struct Test

{
    int Num;
    char* pcName;
    short sDate;
    char cha[2];
    short sBa[4];
}*p;

//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
    printf("%p\n", p + 0x1);
    printf("%p\n", (unsigned long)p + 0x1);
    printf("%p\n", (unsigned int*)p + 0x1);
    return 0;
}
答案是:
10000014
10000001
10000004
  • 首先第一步:通过计算可以得出Test结构体类型的大小为20个字节(不会求的可以查阅鄙人上期相关内容)。

  • 然后看第一个:p是一个结构体类型的指针,0x代表16进制,因此p+1就是指针向后移动了一个结构体类型20个字节。这里可能会有疑问,移动了20个字节,那不应该是10000020吗?这里需要注意的是,%p打印出来的地址是以16进制的方式来表示的,而20=16+4,用16进制来表示正好就是14,所以答案是10000014而不是10000020。

  • 然后第二个:将p强制类型转换为无符号长整形。虽说进行了强制,但也只是强转,并没有改变变量p在内存中存放的数据。由于p变成了无符号长整形,所以p+1就不再是指针的加减了,而是普通的算术加减,p+1的值就为10000001。

  • 第三个也很好办,将p强转为无符号整形指针,p+1就向后移动了4个字节,也就是一个无符号整形的大小,最后的值为10000004。

3. 笔试题NO.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和2000000。这道题还是挺有意思的,不急,听我慢慢道来。
  • 第一个答案应该没有疑问:&a取出的是整个数组的地址,+1后越过数组指向下一位置,强转成整形指针后赋给ptr1,而ptr1[-1]就相当于*(ptr-1)。发现了没有,与我们第一道题一模一样,最后的答案就是最后一个元素4。

  • 有意思的是第二个:a为数组名,代表首元素地址,将a强转为整形然后+1后再强转为整形指针赋给ptr2,与上一题同理,最终ptr2实际上只偏移了1个字节,即指向第一个元素的第二个字节处。那么第一个元素1的第二个字节到底是什么呢?这就要取决于多字节数据在内存中的存储方式了,分为以下两种:

大端模式(big endian):将数据的低字节保存到内存的高地址处
小端模式(little endian):将数据的低字节保存到内存的低地址处
注:一般我们日常使用的计算机都是以小端模式进行存储

那么,根据题目的要求,我们可以画出数组在小端机器上内存的存储情况(数据的表示均为16进制):

  • 由于ptr2是整形指针,解引用就向后访问4个字节依次取出00,00,00,02。而由于我们是小端模式,低地址为数据的低字节位,因此最终*ptr的值就为02000000,与答案相符。

4. 笔试题NO.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。
  • 本题不难,就是要注意到数组初始化里面是小括号而不是花括号。初始化列表里面是三条逗号表达式,每个逗号表达式的值都是最后一个表达式的值,即(0,1)==1;(2,3)==3;(4,5)==5。因此整个数组其实就初始化了3个数,如下:

  • 由于p=a[0],因此p就表示第一个一维数组的数组名,因此p[0]就是这个一维数组的第一个元素1。

5. 笔试题NO.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;
}
答案是FFFFFFFC和-4。本题最好的解决方式是 画图,通过画图我们可以很明显直观的看出答案:
  • 我们画出如上的示意图,其中&p[4][2]与&a[4][2]的位置如上图所示👆注意,p是个指向含4个整形元素的数组的指针,因此p每次+1都越过4个整形的空间。

  • 两个指针相减的结果就是两个指针之间的元素个数。我们看图发现&p[4][2]和&a[4][2]之间有4个元素,由于是低地址减去高地址,因此最后的结果就为-4,而-4用%p来打印就是FFFFFFFC(16进制)

6. 笔试题NO.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。
  • &aa为取出的是数组的地址,+1后越过整个二维数组指向下一位置。然后将其强转为int*指针赋给ptr,此时由于ptr是整形指针,-1偏移一个整形的大小指向数组的最后一个元素,即10。

  • *(aa+1)等价于aa[1],是第二个一维数组的数组名,数组名代表首元素地址,因此ptr2-1就偏移了一个整形的大小指向上一个数组的最后一个元素,即5。

7. 笔试题NO.7

//程序的结果是什么?
#include <stdio.h>
int main()
{
    char* a[] = { "work","at","alibaba" };
    char** pa = a;
    pa++;
    printf("%s\n", *pa);
    return 0;
}
答案是at
  • 字符串常量的字面值是其首元素的地址。因此a数组的元素实际上是三个char*类型的指针,每个char*指针指向对应的字符串,如下:

  • a是数组名,代表首元素地址,由于首元素是char*类型的指针,因此用二级指针pa接收。

  • pa是首元素地址,+1后即为第二个元素的地址,解引用后即为第二个元素。结合上图我们可以得出第二个元素就是字符串at的首元素地址,因此按照%s格式打印后结果为at。

8. 笔试题NO.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
  • 我的天,这是嘛呀,太吓人了吧这些表达式!不急,我们画个图先:

  • 根据题目我们画出了如上关系图👆。首先是第一个:cpp指向cp数组的第一个元素,++cpp即指向cp数组第二个元素,因此*++cpp值为c+2。而c+2又指向c[2],因此**++cpp最终就为c[2]。c[2]为字符串"POINT"的首元素地址,按照%s来打印即为POINT。

  • 然后是第二个:需要注意的是,前一条语句中cpp已经++自增一次了,自增后关系图如下:

  • 同理,++cpp即指向cp的第三个元素,解引用后即为c+1,而c+1指向c[1],因此再--后就为c[0],即*--*++cpp的结果为c[0],c[0]是字符串"ENTER"的首元素地址,+3后即指向字符E,用%s打印后结果为ER。

  • 接着是第三个:依旧要根据上条语句的自增自减语句对关系图进行更新:

  • cpp[-2]可以等价为*(cpp-2),即为c+3,再对其解引用后为c[3],c[3]为字符串"FIRST"的首元素地址,+3后就指向字符S,用%s打印后结果为ST。

  • 最后是第四个:由于上一步没有自增自减语句,因此关系图不变:

  • cpp[-1][-1]可以等价为*(*(cpp-1)-1)。cpp-1指向cp第二个元素,解引用后即为c+2。然后c+2指向c数组的第3个元素,-1并解引用后即为c数组的第2个元素c[1]。然后c[1]为字符串"NEW"的首元素地址,+1后即指向字符E,用%s打印后结果为EW。

三. 总结✈

做完这8道题目,不得不说,画图真的是一个非常好的解题方法。有些题目,尽管看似十分复杂,当我们把示意图画出来时,问题就迎刃而解,非常直观。因此 我们在日常分析题目时要擅于画图。
通过这三期的学习,相信我们对指针有了较深的理解,赶紧下去尝试尝试吧,可千万不要一看就会,一写就废 😁

以上,就是本期的全部内容啦🌸

制作不易,能否点个赞再走呢🙏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/3717.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络】https协议

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【网络】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文章…

使用stm32实现电机的PID控制

使用stm32实现电机的PID控制 PID控制应该算是非常古老而且应用非常广泛的控制算法了&#xff0c;小到热水壶温度控制&#xff0c;大到控制无人机的飞行姿态和飞行速度等等。在电机控制中&#xff0c;PID算法用的尤为常见。 文章目录使用stm32实现电机的PID控制一、位置式PID1.计…

python机器学习课程——决策树全网最详解超详细笔记附代码

文章目录决策树算法一、简介1.概述2.决策树学习3.典型算法4.基本思想5.构造方法6.基本算法二、ID3决策树1、信息熵2、条件熵3、信息增益4、总结5、决策树进行分类的步骤三、ID3决策树示例1、数据集2、计算经验熵&#xff08;香农熵&#xff09;3、计算信息增益4、树的生成5、树…

YOLOv7训练自己的数据集(手把手教你)

YOLOv7训练自己的数据集整个过程主要包括&#xff1a;环境安装----制作数据集----模型训练----模型测试----模型推理 一&#xff1a;环境安装 conda create -n yolov7 python3.8 conda activate yolov7 #cuda cudnn torch等版本就不细说了&#xff0c;根据自己的显卡配置自行…

JVM类加载机制

文章目录定义类加载过程加载链接验证准备解析初始化类加载器双亲委派模型定义 Java 虚拟机把描述类的数据从 Class 文件加载到内存&#xff0c;并对数据进行校验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的 Java 类型&#xff0c;这个过程被称为虚拟机的类…

LeetCode:202. 快乐数

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;202. 快乐数 题目描述&#xff1a;编写一个算法来判断一个数 n 是不是快…

进程间通信【Linux】

文章目录1. 进程间通信1.1 什么是进程间通信1.2 进程间通信的必要性1.3 进程间通信的本质1.4 进程间通信的方式2. 匿名管道2.1 匿名管道的概念2.2 匿名管道的原理注意2.3 实现匿名管道pipe函数步骤1. 创建管道2. 创建子进程3. 构建单向信道子进程父进程构建一个变化的字符串写入…

基于微信小程序+爬虫制作一个表情包小程序

跟朋友聊天斗图失败气急败坏的我选择直接制作一个爬虫表情包小程序&#xff0c;从源头解决问题&#xff0c;从此再也不用担心在斗图中落入下风 精彩专栏持续更新↓↓↓ 微信小程序实战开发专栏 一、API1.1 项目创建1.2 图片爬虫帮助类1.3 测试窗体1.4 接口封装二、小程序2.1 项…

【数据结构】实现二叉树的基本操作

目录 1. 二叉树的基本操作 2. 具体实现 2.1 创建BinaryTree类以及简单创建一棵树 2.2 前序遍历 2.3 中序遍历 2.4 后序遍历 2.5 层序遍历 2.6 获取树中节点的个数 2.7 获取叶子节点的个数 2.8 获取第K层节点的个数 2.9 获取二叉树的高度 2.10 检测值为val的元素是否…

“中国李宁“,能否救李宁?

随着国潮热兴起&#xff0c;马拉松赛事的重启&#xff0c;李宁、安踏等国产运动品牌迅速崛起成为主流。▲图源&#xff1a;无锡马拉松官方3月17日&#xff0c;“国潮运动”元老&#xff0c;李宁有限公司(下称“李宁”,02331.HK)发布了2022年的业绩报告。在国内消费环境不景气等…

Python满屏表白代码

目录 前言 爱心界面 无限弹窗 前言 人生苦短&#xff0c;我用Python&#xff01;又是新的一周啦&#xff0c;本期博主给大家带来了一个全新的作品&#xff1a;满屏表白代码&#xff0c;无限弹窗版&#xff01;快快收藏起来送给她吧~ 爱心界面 def Heart(): roottk.Tk…

到了这个年纪,就应该阅读Spring源码了,源码阅读指南-编译加运行

文章目录到了那个年纪&#xff0c;就应该阅读Spring源码了&#x1f604;第一步&#xff0c;clone&#x1f606;第二步&#xff0c;使用idea打开项目&#x1f60a;gradle介绍&#xff08;插叙手法&#xff09;&#x1f603;第三步&#xff0c;修改gradle的远程仓库地址&#x1f…

如果大学能重来,我绝对能吊打90%的大学生,早知道这方法就好了

最近收到很多大学生粉丝的私信&#xff0c;大多数粉丝们都迷茫着大学计算机该怎么学&#xff0c;毕业后才能找到好工作。 可能是最近回答这方面的问题有点多&#xff0c;昨晚还真梦回大学…其实工作了20多年&#xff0c;当过高管&#xff0c;创过业&#xff0c;就差没写书了。…

【蓝桥杯_练习】

蓝桥杯1.创建工程2.LED灯点亮led.c3.LCD液晶屏显示lcd.c4.定时器按键单机interrupt.hinterrupt.cman.c5.定时器&#xff08;长按键&#xff09;interrupt.hinterrupt.cmain.c6.PWMmain.c7.定时器-输入捕获&#xff08;频率&#xff0c;占空比测量&#xff09;interrupt.cmain.c…

STA:时序检查 - reg2output型

目录3. reg2output3.1. 建立时间TsetupT_{setup}Tsetup​检查3.2. 保持时间TholdT_{hold}Thold​检查作STA要分析的时序路径四种中的一种&#xff0c;可看作是reg2reg的变种。详见STA&#xff1a;时序检查 3. reg2output 第三种是发起触发器在设计内部&#xff0c;捕获触发器在…

Java语言-----封装、继承、抽象、多态、接口

目录 前言 一.封装 1.1封装的定义 1.2访问修饰符的使用 二.继承 2.1继承的定义 2.2继承的方法 2.3继承使用注意点 三.多态 3,1多态的定义 3.2动态绑定 3.3方法重写 3.4向上&#xff08;向下&#xff09;转型 四.抽象 4.1抽象的概述和定义 4.2抽象的使用 五…

Idea+maven+spring-cloud项目搭建系列--11 整合dubbo

前言&#xff1a; 微服务之间通信框架dubbo&#xff0c;使用netty &#xff08;NIO 模型&#xff09;完成RPC 接口调用&#xff1b; 1 dubbo 介绍&#xff1a; Apache Dubbo 是一款 RPC 服务开发框架&#xff0c;用于解决微服务架构下的服务治理与通信问题&#xff0c;官方提…

肠道菌群对药物,重金属,污染物,膳食化合物的代谢和健康效应

谷禾健康 肠道菌群和人体健康息息相关&#xff0c;我们经常讲饮食、生活方式等都可以影响肠道菌群的组成&#xff0c;除了这些耳熟能详的因素之外&#xff0c;其他异源物如环境中的污染物&#xff0c;重金属&#xff0c;药物等都会影响肠道菌群&#xff0c;反过来&#xff0c;细…

antd+vue——table组件字段排序——对象数组排序 中文排序——基础积累

最近在用vueantd做后台管理系统时&#xff0c;遇到一个需求就是要实现table表格某个字段的排序 效果图如下&#xff1a; 在antd官网上&#xff0c;有关于排序的示例&#xff1a; 示例中给出的两个字段的排序 监听change事件&#xff0c;可以获取到当前排序的字段名 由于我这…

100天精通Python(可视化篇)——第81天:matplotlib绘制不同种类炫酷饼图参数说明+代码实战(自定义、百分比、多个子图、圆环、嵌套饼图)

文章目录专栏导读0. 前言1. 参数说明2. 普通饼图3. 百分比饼图4. 突出某一块的饼图5. 自定义颜色的饼图6. 多个子图7. 圆环饼图8. 圆环分离饼图9. 饼图圆环图组合10. 多层圆环饼图专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#xff1a…
最新文章