(番外篇)指针的一些相关习题讲解(速进,干货满满)(2)

news2025/1/12 9:37:29

前言:

  小编感觉最近有点太堕落,于是我开始从事这篇文章的撰写,现在也是进入七月份了,我现在文章开头定一个小目标,我决定在七月份发布至少十篇文章,希望我可以说到做到(我前面就口头欠了不少文章),好了,不多废话了,下面进入今天的主题,对于指针习题的演练,这些题都是小编在上课时听到的题,为了加强自己的记忆,于是我将此作为文章的主题,下面进入正文喽

目录:

1.题目一

2.题目二

3.题目三

4.题目四

5.题目五

6.题目六

7.题目七

 正文:

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

  各位先来思考一下这个题怎么做,老规矩,小编依旧是先解释,后做题,下面进入解释环节:首先我们先看printf函数,里面第一个元素是*(a + 1),此时a是数组名,它不是那两种特殊情况,所以此时数组名代表的就是数组第一个元素,加1后对应着数组第二个元素的地址,在进行解引用操作后,会直接呈现第二个元素,那么就是2了;之后是第二个元素,此时涉及到了ptr,所以我们先看ptr这个指针,它代表着进行整型强制类型转换以后的(&a + 1),不难发现,此时数组名符合了第一种特殊情况,所以代表着整个数组的地址,加1后是数组最后一个元素下行的地址,此时指针指向的应该是5后面元素的地址,此时减去1后,又回到了数组第五个元素代表的地址,所以进行解引用操作后,变成了5,为了让读者更好的理解这个题目,小编特意的给了图例进行解释:

 

  通过上图可以清晰的看出小编在文字解释中所解释的内容,下面小编展示这个代码的运行图:

  可以看出小编解释的是正确定,下面废话不多说,进入下一个题的训练:

2.题目二 

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
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;
}

    老规矩,大家先思考几秒,然后给出解释,下面开始解释环节:首先映入我们眼帘的是一个结构体,由于结构体的大小如何计算小编还没有设计到,所以这里直接给大小了(后面小编会着重强调它的对齐方式的在结构体的文章中),这里我们已经给出了结构体的地址,不过这个地址得强制类型转换,不然就不是结构体的地址了,下面我们正是看看函数部分,首先是第一个printf函数,这个函数内部是结构体指针加1,记住这个加的1是0x开头的,所以应该是16进制的方式进行操作的,此时p是指针,指针加数的话代表着它的地址是要加它本身大小倍数的数,所以此时应该加20个字节,并且由于这是16进制储存的,所最后的结果应该是0x100014;对于第二个printf函数,首先我们先看括号内容,此时括号内容是unsigned long,所以此时的p已经强制类型转换成无符号的长整型了,此时p可以看作一个整数,对于整数加1的话,那就是真的加1,所以结果应该是0x100001;对于第三个printf语句,此时括号里面的内容是unsigned int*,代表着强制类型转换成无符号整型的指针,所以此时指针每次加的大小已经改变了,此时加1代表着加4个字节的空间,所以结果应该是0x100004,这个问题小编认为不需要图来了解,因为这没有牵扯到指针的迁移,仅仅就牵扯到了指针运算,下面来看看此代码的运行图:

   读者朋友无需关注前面是什么,光看末尾数字就好了,此时说明小编解释没有错误,那么我们加快步伐,直接开始看下一个题的讲解!

3.题目三 

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

  老规矩,各位先看会题目,我等会做出解释,下面开始解释:首先这个题定义的二维数组,然后设置了一个整型指针,此时指针指向二维数组第一行的地址,然后printf函数里面的是p[0],此时其实有第二种写法,就是*(*p + 0),此时不难看出,这个是指向数组第一个元素的地址,那么我们回头看看这个二维数组,可能有些读者会脱口而出,答案是0,但是其实这个说法是错误的,仔细看,数组里面每两个元素用什么围起来的,是括号,而不是大括号!所以此时我们对于括号里面的内容,其实是逗号表达式,逗号表达式的逻辑是,从左往右依次计算,不过最后的结果是右边的数,所以其实这个数组在内存中的存放是如下图所示的:

  所以可以知道打印出来的结果应该是1,那么很多读者可能忘记了我们想要规定二维数组行和列具体数应该怎么做,其实这里是要用大括号的,下面是代码展示:

int a[3][2] = { {0,1 }, {2,3} , {4,5} };

  下面是在调试界面此数组的存放:

  所以各位在做相关习题的时候一定要瞪大双眼,防止出题人给你设置成一个大坑让你跳进去,我们要做到看到坑直接绕过去,下面不多废话直接上运行页:

   可以看出小编并没有说错,大家一定要在做题不要看题目过于简单而痛失分数。下面不多废话,调整心态,下个题来喽:

4.题目四

//假设环境是x86环境,程序输出的结果是啥?
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;
}

  这个题一看就比上面的题复杂了很多,毕竟难度是相加的,在讲解这个题之前,小编还想告诉大家,大家在做指针相关习题的时候一定要记得画图,画图可以帮助我们更好的了解这个题怎么做,小编就在每次在牵扯到指针计算的时候,都会画上图来帮助大家理解,其实很显然,图文要比文字更好的来帮助人们去理解知识,所以大家在做题的时候一定要画图,下面来开始解释环节了。

  由于这个题太过复杂,小编先放上a数组的存放图文来帮助大家去感受a数组内容:

   下面我们来正式开始观看这个代码,首先我们看到一个老朋友:int (*p)[4],读者朋友如果已经学完了指针部分,那么很容易看得出这个是数组指针,p代表的是数组,而[4]则代表着里面存放着四个元素,下面我们可以接着看,此时把a首行的地址给了p,此时读者朋友可能会想,a每个里面存放着5个元素,而p里面却存放着4个元素,它们如何建立起联系呢?其实这里不用多考虑,首先,p其实在这里我们可以看做成地头蛇,有句老话这么说,在我的地盘,是龙只能盘着,是虎也得趴着,所以此时每个p里面都存放每个a所对应的四个元素,此时小编也将p在内存中的存放也放到下面了:

  下面我们已经画好了a和p的图,此题其实已经完成了大半部分了,下面我们来看看printf函数里面的内容,这两个元素是一样的,只不过是两种形式表示着,一个是地址,一个是整型,下面我们先来看看元素是什么,分别是p[4][2]和a[4][2],如果没有画图,可能很难看出来这个具体表示着什么,但是现在我们有了图,这就好办了,下面是小编利用图来表示这两个元素 

  此时printf里面存放着是这两个数的地址,小编之前说过,指针和指针相减,得出来的结果是两个指针之间相差的元素的个数,这个小编之前写过的文章说过,下面放上链接,感到兴趣的读者朋友可以看一下:深入理解并打败C语言难关之一————指针(1)-CSDN博客,回归正题,这两个数之间相差的个数是4个,但这个是前者减去后者,所以得出来的结果应该是-4,不过现在的重点是第一个元素是要取到它的地址,所以这里牵扯到了原码反码补码的知识,这些知识小编也会在以后的文章说的(欠的文章+1),所以下面通过代码页来展示-4的反码:

//10000000000000000000000000000100  //原码
//11111111111111111111111111111011 //反码  (原码取反)
//11111111111111111111111111111100   //补码(反码 + 1)

  所以的出来结果应该是下面源码4个为一起通过16进制位数表示出来,应该是0xFFFFFFFC,下面这个题已经讲解完毕,下面进入此题目的运行环节:

  可以看出小编这里并没有说错,这个题有点复杂,读者朋友们如果理解不了的话一定要多看一遍,下面趁热打铁,进入下个题目喽!

 5.题目五

int main()
{
	int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)(*(a + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

  如果会了上一个题的话,这个题也是会迎刃而解的,老规矩,读者朋友先思考下这个题,等会小编做出解释,三,二,一,下面给出解释:

  首先,印入我们眼帘的还是一个二维数组,下面小编先来画一下二维数组的储存图来帮助各位取理解:

  这个二维数组的存放就是如图所示的(为了直观小编直接展开成一行了),我们开始看代码页,首先我们先看到的是两个指针,ptr1和ptr2,首先第一个是先对数组名进行取地址操作,此时涉及到了小编之前提到过的特殊情况,所以此时是取到了整个数组的地址,在进行加1就是去到了10以后元素的地址,此时还对他强制类型转化成了整型指针(可能很多读者朋友会很奇怪,认为二维数组不也是整型指针吗,其实二维数组实质是一个数组指针,小编以前可能提到过,如果没提到过大家记住就好),在看ptr2,此时这个指针同样也是对括号里面的内容强制类型转化成整形指针了,下面我们再来看看括号里面的内容,此时是先对a进行加1操作,我们知道,二维数组数组名代表着第一行的地址,所以这个进行加1操作以后直接变成了a[1],在进行解引用操作以后直接变成了第一个元素所代表的地址了,下面为了让读者朋友更好的理解这个题目,直接上图:

  之后我们继续往下看,我们来看看printf函数,此时里面同样也是有两个元素,第一个是对ptr1 - 1,此时ptr已经是整形指针了,所以减1后对应着10这个元素的地址,然后解引用后会变成10;第二个是ptr2 - 1 ,此时ptr2也是整形指针,减去1后代表着5的地址,解引用后是5,下面我们先用图文解释,在来放运行图:

 

  可以看出小编并没有说错,大家在写题目的时候一定要画图,画图可以保证大家做题目时更有思路,提高正确率,下面我们进入下一道题:

6.题目六

 

int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
 }

  这个题可能会有一点难度,读者朋友一定要先仔细看看这个题,画出相应的图这个题就好;理解多了,下面小编放上这个题的解析:

  首先,我们看到了一个数组我,这个数组的类型是char *,所以很显然这个数组是个指针数组,下面我们图解这个指针数组:

  通过上图可以清晰的看出这个指针数组的具体内容,之后我们接着看代码,后面的代码是又设置了一个二级指针pa,这个二级指针储存着此指针数组首元素地址的地址,接着看下面的代码,此时是pa++,代表着pa + 1,所以此二级指针变成了储存第二个元素的地址的地址,然后我们继续看下面代码,看出其元素是对二级指针进行解引用,我们知道,二级指针解引用后就变成了其储存元素的地址,此时由于储存着字符串,对字符串的打印我们需要获取字符串的地址,此时正好是第二个元素的地址,所以应该打印出的是"at",下面小编先给图解,然后给运行图:

 

  以上便是运行结果和图解,所以小编这里又一次得强调下,对于这种题一定要画图,画图可以帮助我们解决大部分我们无法解决的问题,下面我们趁热打铁,迎接本篇文章最后一个习题,此习题与本习题同一个做法,大家一定要理解好这个题的做法: 

题目七

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;
}

  这个题的难度比上面的难度还要大,大家先看看这个代码,自己画图思考一下,下面小编着重进行解释了:

  首先,我们不难发现了这里牵扯到了三个指针,分别是指针数组,二级指针,三级指针,各位先看好这几个指针,下面小编直接上这三个指针的图解了,毕竟一个一个写显得我有点水字数了:

 

  下面我们来看看printf语句里都有什么元素,首先看第一个printf里面的元素,里面是 ** ++ cpp,首先我们知道++操作符运算级是很高的,所以此时是cpp先 加 1,然后此时指向了cp[1]的地址,之后二次解引用操作后,变成指向了c[2]的地址,所以是打印了c[2]所代表的字符串,也就是"POINT",下面小编给上图解:

   下面我们再来看看第二个printf函数里面的元素,这个更是复杂:*-- * ++cpp + 3,此时我们一个一个的看,此时是先进行 ++ cpp的操作,经过上面的printf里面cpp进行加1后,此时在进行加1,cpp指向了cp[2]的地址,此时进行解引用操作后就会变成cp[2],之后我们在进行--操作,此时是代表着cp[2]所指向的内容减1,所以此时cp[2]变成了指向c[0]的地址,再次进行解引用操作以后,此时就是变成了c[0],此时再次进行 + 3操作,代表着数组里面的元素加3,所以我们打印数据就开始从E进行打印,所以最后打印的结果应该是“ER”,下面给上图解:      下面我们继续来看看下一行的代码,此时里面的元素是 : *cpp[-2] + 3 ,我们一九从左往右看,此时是先对于cpp进行解引用操作,由于上面进行了两次 ++ 操作,所以解引用cpp以后里面的元素变成了cp[2],此时有一个[]操作符,其实这个就是再次对cp进行解引用,不过是减2后再解引用,所以此时变成了cp[0],进行解引用后就指向了c[3],此时再次加3代表着里面元素地址加3,所以是从“S”开始打印,最后打印出来的应该是:"ST",下面依旧给上图解来帮助大家理解:

  下面我们再来看最后一个printf函数里面的元素,里面是:cpp[-1][-1] + 1 ,我们依旧从左往右看,此时cpp[-1]其实实际代表着* cpp - 1,所以此时是代表着cp[1],之后的[- 1],代表着 * cp[1] - 1,也就是说最后指向的是c[1],此时在进行 + 1操作后,就代表着从“E”开始进行打印,最后呈现的结果是 “EW”,下面我们依旧用图文进行解释:

   下面我们来放运行结果:

     可以看出雄小编这个题并没有说错,这个题大家一定要好好的去了解,难度系数还是比较大的,这个题也是同样彰显了画图的重要性,大家在做题时一定要好图,这样才不容易出错,也好找到自己的错误。

总结:

  小编也是干完了这篇文章,这也是指针部分的强化篇,指针讲解部分到这里就彻底的结束了,相信各位读者朋友看到小编写过的指针篇幅,大概也知道了指针的重要性,大家一定要好好掌握指针,到现在开始,我们已经返过了C语言的一座大山,希望各位已经明白了指针。最后,如果文章有什么错误,请大家一定在评论区指出,小编会改正自己错误的,那么,我们下篇博客见啦! 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不同系统间数据交换要通过 api 不能直接数据库访问

很多大数据开发提供数据给外部系统直接给表结构,这是不好的方式。在不同系统间进行数据交换时,通过API(应用程序编程接口)而非直接访问数据库是现代系统集成的一种最佳实践。 目录 为什么要通过API进行数据交换如何通过API进行数据…

RFID技术在印度铁路上的革新应用

Kavach系统是一种利用RFID技术的先进列车事故防撞系统,特别设计用于印度铁路网络,旨在减少在同一轨道上运行的列车事故。最近在西孟加拉邦大吉岭地区发生的可怕火车事故再次突显了这一系统的重要性和必要性。 Kavach系统由研究设计与标准组织&#xff08…

Android 抓取 CPU 资源信息

在 Android 开发中,使用 ADB(Android Debug Bridge)命令获取 CPU 资源信息有很多重要的作用。这些命令可以帮助开发者在多种情况下分析和优化应用性能、解决问题以及进行系统性调试。 以下列举一些 ABD 获取 CPU 资源信息的命令 获取 CPU 核…

MYSQL substring_index

1.substring_index( 参数1,参数2 ,参数3 ) 2.group by 也可以用我们起的别名来划分,以及起别名可以不用as SELECT IF(profile LIKE %female,female,male) gender,COUNT(*) number FROM user_submitGROUP BY gender; 3.切割、截取、删除、替换 select -- 替换法 r…

ffmpeg在powershell和ubuntu终端下的不同格式

在win10下的powershell中,如果想运行一个exe文件,就不能再像cmd命令行一样用名字来直接运行了,否则会提示格式不对。 正确的做法是: . \ffmpeg.exe -re -i video-test.mpr -rtsp_transport tcp -vcodec h264 -f rtsp rtsp://您的…

数据库管理系统中的磁盘、文件、页和记录管理

1. 引言 数据库管理系统(DBMS)是一个复杂的软件系统,用于管理和操作数据库中的数据。DBMS需要有效地在磁盘和内存之间组织和管理数据,以确保高效的数据存储和检索。本文将详细介绍DBMS中关于磁盘、文件、页和记录的管理&#xff…

模电-运放的供电

模电-运放的供电 Fang XS.1452512966qq.com如果有错误,希望被指出,学习技术的路难免会磕磕绊绊量的积累引起质的变化注:本文章为唐老师讲电赛视频的个人笔记 运放的供电 所有的运放都可以单电源和双电源供电;采用双电源供电的运…

【Python画图-驯化seaborn】一文搞懂seaborn中的箱线图实践技巧

【Python画图-驯化seaborn】一文搞懂seaborn中的箱线图实践技巧 本次修炼方法请往下查看 🌈 欢迎莅临我的个人主页 👈这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合,智慧小天地! 🎇 免费获取相关内容文档关注&a…

Spring Boot集成geode快速入门Demo

1.什么是geode? Apache Geode 是一个数据管理平台,可在广泛分布的云架构中提供对数据密集型应用程序的实时、一致的访问。Geode 跨多个进程汇集内存、CPU、网络资源和可选的本地磁盘,以管理应用程序对象和行为。它使用动态复制和数据分区技术…

基于MIMO系统的预编码matlab性能仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 最小均方误差(MMSE)准则 4.2 量化准则 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 …

for循环中list触发fast-fail或不触发的原理和方法

Iterable和Iterator Iterator接口位于的位置是java.util.Iterator,它主要有两个抽象方法供子类实现。hasNext()用来判断还有没有数据可供访问,next()用来访问下一个数据。 集合Collection不是直接去实现Iterator接口,而是去实现Iterable接口…

Stable Diffusion web UI 插件

2024.7.3更新,持续更新中 如果需要在linux上自己安装sd,参考:stable diffusion linux安装 插件复制到 /stable-diffusion-webui/extensions 目录下,然后重新启动sd即可 一、插件安装方法 每种插件的安装方法可能略有不同&#xf…

java内存管理机制(二)-内存分配

在上一篇文章中,我们花了较大的篇幅去介绍了JVM的运行时数据区,并且重点介绍了栈区的结构及作用,在本文中,我们将主要介绍对象的创建过程及在堆中的分配方式。 对象的创建 在上文我们提过一些问题,你的对象是怎么new…

bWAPP靶场安装

bWAPP安装 下载 git地址:https://github.com/raesene/bWAPP 百度网盘地址:链接:https://pan.baidu.com/s/1Y-LvHxyW7SozGFtHoc9PKA 提取码:4tt8 –来自百度网盘超级会员V5的分享 phpstudy中打开根目录,并将下载的文…

【python】PyQt5事件机制、定时器原理分析和实战演练

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

基于LLM(Large Language Model,大语言模型)的智能问答系统

基于LLM(Large Language Model,大语言模型)的智能问答系统是一种利用先进的人工智能技术,尤其是自然语言处理(NLP)技术,来构建能够理解和回答用户问题的系统。这种系统通过训练大量文本数据&…

德国Testing Expo丨落幕不散场!知迪展台风采回顾

德国斯图加特国际展览中心,随着全球汽车产业的目光聚焦,Automotive Testing Expo Europe 2024圆满落幕。在这场汇聚了全球顶尖汽车测试技术的盛会中,知迪科技凭借卓越的技术实力和前瞻性的解决方案,成为了现场诸多专业观众的瞩目焦…

pydub、ffmpeg 音频文件声道选择转换、采样率更改

快速查看音频通道数和每个通道能力判断具体哪个通道说话;一般能量大的那个算是说话 import wave from pydub import AudioSegment import numpy as npdef read_wav_file(file_path):with wave.open(file_path, rb) as wav_file:params wav_file.getparams()num_cha…

红酒与舞蹈:舞动的味觉艺术

在艺术的海洋中,红酒与舞蹈总是能激起人们心中较温柔的涟漪。红酒以其深邃的色泽、馥郁的香气,诠释着味觉的艺术;而舞蹈,则以优雅的姿态、灵动的步伐,演绎着视觉的盛宴。当红酒遇上舞蹈,一场别开生面的艺术…

Ubuntu防火墙相关内容

Ubuntu防火墙相关的命令,主要用于日常使用过程中,忘记命令时查找方便,不用再去各种地方搜索了。以下命令均已root用户执行,如果是非root用户,需要添加sudo 查看防火墙的启用状态 ufw status 说明是启用状态。 启用防…