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

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

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

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

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

相关文章

RTL8201 以太网PHY芯片 调试记录

一、概述 为了尽量给甲方降低成本&#xff0c;决定使用较低成本的PHY芯片RTL8201F-VB-CG芯片。移植官网的以太网demo程序&#xff0c;git上下载了一份很好看的rtl8201F的驱动程序&#xff0c;用来替换官方demo的lan8742程序。并没有直接通&#xff0c;于是开始了调试之路。 二…

mysql索引类型有哪些?

在Mysql数据库当中&#xff0c;我们经常会谈到Sql语句&#xff0c;当然也会谈到索引优化&#xff0c;那么在数据库当中有哪些索引类型呢&#xff0c;博主在这里进行分享&#xff0c;希望对大家能有所帮助。 目录 1、B-Tree索引&#xff1a; 2、Hash索引&#xff1a; 3、Full…

SpringBoot 将PDF转成图片或World

SpringBoot 将PDF转成图片或World 准备工作Apache PDFBox将PDF转成一张图片将PDF转成多张图片将PDF转成其他文件格式总结SpringBoot 是一款非常流行的 Java Web 开发框架,可以用来构建各种 Web 应用程序。在本篇博客中,我们将介绍如何使用 SpringBoot 将 PDF 转换成图片或其他…

Elasticsearch 学习+SpringBoot实战教程(三)

需要学习基础的可参照这两文章 Elasticsearch 学习SpringBoot实战教程&#xff08;一&#xff09; Elasticsearch 学习SpringBoot实战教程&#xff08;一&#xff09;_桂亭亭的博客-CSDN博客 Elasticsearch 学习SpringBoot实战教程&#xff08;二&#xff09; Elasticsearch …

第十四届蓝桥杯三月真题刷题训练——第 23 天

目录 第 1 题&#xff1a;长草 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 思路&#xff1a; 第 2 题&#xff1a;蓝肽子序列_LCS_最长公共子序列dp问题 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 思路&am…

Spring源码面试最难问题——循环依赖

前言 问&#xff1a;Spring 如何解决循环依赖&#xff1f; 答&#xff1a;Spring 通过提前曝光机制&#xff0c;利用三级缓存解决循环依赖&#xff08;这原理还是挺简单的&#xff0c;参考&#xff1a;三级缓存、图解循环依赖原理&#xff09; 再问&#xff1a;Spring 通过提前…

【前沿技术】问答pk【ChatGPT Vs Notion AI Vs BAT AI 】

目录 写在前面 问题&#xff1a; 1 ChatGPT 1.1 截图 ​1.2 文字版 2 Notion AI 2.1 截图 2.2 文字版 3 BAT AI 3.1 截图 3.2 文字版 总结 序言 所有幸运和巧合的事&#xff0c;要么是上天注定&#xff0c;要么是一个人偷偷的在努力。 突发奇想&#xff0c;问三个…

③【Java组】蓝桥杯省赛真题 持续更新中...

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 蓝桥杯真题--持续更新中...一、错误票据题目描…

CCF-CSP认证 202303 500分题解

202303-1 田地丈量&#xff08;矩阵面积交&#xff09; 矩阵面积交x轴线段交长度*y轴线段交长度 线段交长度&#xff0c;相交的时候是min右端点-max左端点&#xff0c;不相交的时候是0 #include<bits/stdc.h> using namespace std; int n,a,b,ans,x,y,x2,y2; int f(in…

用CSS3画了一只猫

感觉我写得技术含量不高&#xff0c;全都是用绝对定位写的&#xff0c;一定会有更好的&#xff0c;代码量更少的做法吧 <!DOCTYPE html> <html> <head><title>Cute Cat</title><style type"text/css">*{box-sizing: border-box…

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

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

【VScode】远程连接Linux

目录标题1. 安装扩展插件2. 在Linux上操作3. 确定Linux的IP地址4. 远程连接到Linux5. 实现免密码登录使用 VScode 远程编程与调试的时有会用到插件 Remote Development&#xff0c;使用这个插件可以在很多情况下代替 vim 直接远程修改与调试服务器上的代码&#xff0c;同时具备…

超详细讲解C语言文件操作!!

超详细讲解C语言文件操作&#xff01;&#xff01;什么是文件文件名文件的打开和关闭文件指针文件的打开和关闭文件的顺序读写文件的随机读写fseekftellrewind文本文件和二进制文件文件读取结束的判定文件缓冲区什么是文件 磁盘上的文件是文件。但是在程序设计中&#xff0c;我…

Python | 蓝桥杯系列文章总结+经典例题重做

欢迎交流学习~~ 专栏&#xff1a; 蓝桥杯Python组刷题日寄 从 4 个月前开始写蓝桥杯系列&#xff0c;到目前为止一共是 19 篇&#xff0c;其中&#xff1a;入门篇 5 篇&#xff0c;简单篇 8 篇&#xff0c;进阶篇 6 篇。 这篇文章主要是为了为先前内容进行总结&#xff0c;并对…

蓝桥杯冲刺 - Lastweek - 你离省一仅剩一步之遥!!!(掌握【DP】冲刺国赛)

文章目录&#x1f4ac;前言&#x1f3af;week3&#x1f332;day10-1背包完全背包多重背包多重背包 II分组背包&#x1f332;day2数字三角形 - 线性DP1015. 摘花生 - 数字三角形&#x1f332;day3最长上升子序列 - 线性DP1017. 怪盗基德的滑翔翼 - LIS1014.登山 - LIS最长公共子…

【JaveEE】多线程之阻塞队列(BlockingQueue)

目录 1.了解阻塞队列 2.生产者消费者模型又是什么&#xff1f; 2.1生产者消费者模型的优点 2.1.1降低服务器与服务器之间耦合度 2.1.2“削峰填谷”平衡消费者和生产的处理能力 3.标准库中的阻塞队列&#xff08;BlockingQueue&#xff09; 3.1基于标准库&#xff08;Bloc…

笔记本只使用Linux是什么体验?

个人主页&#xff1a;董哥聊技术我是董哥&#xff0c;嵌入式领域新星创作者创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01;近期&#xff0c;也有朋友问我&#xff0c;笔记本只安装Linux怎么样&#xff0c;刚好我也借此来表达一下我的感受…

数据结构MySQL —— 索引

目录 一、索引概述 二、索引结构 三、索引分类 四、索引语法 五、SQL性能分析 1. 查看执行频次 2. 慢查询日志 3. show profiles指令 4. explain执行计划 六、索引使用规则 1. 验证索引效率 2. 最左前缀法则 3. 范围查询 4. 索引失效情况 5. SQL提示 6. …

【C++】AVL树

文章目录一、什么是 AVL 树二、AVL 树的节点结构三、AVL 树的插入四、AVL 树的旋转1、左单旋2、右单旋3、左右双旋4、右左双旋5、总结五、VAL 树的验证六、AVL 树的删除七、AVL 树的性能八、AVL 树的代码实现一、什么是 AVL 树 我们在前面学习二叉搜索树时提到&#xff0c;二叉…

【linux】深入了解TCP与UDP

认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理; IP地址 端口号能够标识网络上的某一台主机的某一个进程; 一个端口号只能被一个进程占用理解 "端口号" 和…