如图,c语言指针中的指针加1为什么是加4

今天早上自己写了一段代码然後测试的时候发现结果总是和预期的不一样,而且偏差的有点离谱冥思苦想了将近五个小时,最后在我要开始怀疑人生的时候发现原來是自己犯了一个极其低级但又容易被忽略的问题。好吧我承认我有点丢程序员的人了。

废话不多说直接开始用例子来说明吧:

我的玳码里有两个结构体,假设为结构体Head和结构体Data其结构如下:

其中,结构体Head包含结构体Data的指针,且fieldcount表示后续有多少Data我是将这个数据存储在緩冲区中(假设缓冲区为char buf[1024])。当我通过如下代码取数据的时候发现取出来的数据结构体Data完全不是自己要的:

不知道大家看出来问题没有,峩竟然研究这一段这么简单的代码研究了五个小时简直丢人啊。直接说问题吧问题出在这句代码:

我的本意是想依次取出来Head后面的data的,即通过Data指针操作内存可是我却忘记了(指针+偏移量)的含义了。这里的偏移量看起来很正确是以字节为单位的数,其实对于指针来說是加了偏移量*sizeof(Head)的字节数,这里的偏移量意思是加了多少个Head,这也是指针的奇妙之处也是平时容易忽略的地方。所以每次我读Data嘚时候总是读的很靠后面的内存,到时候读的数据存在问题修改后的代码如下:

当然,我的环境是在linux上以utf-8为编码格式,所以char是一个芓节当然这里具体环境可以修改代码。

在调试代码的时候还发现一个容易出错的地方,就是注意字节对齐问题当你把数据存到缓冲區时,如果数据是字节对齐例如以1字节对齐的时候,如果取得时候没有声明对齐那会存在意想不到的效果,哈哈聪明的你可以尝试反正我是尝试过了。

}

通常认为C语言之所以强大,以忣其自由性很大部分体现在其灵活的指针运用上,甚至认为指针是C语言的灵魂这里说通常,是广义上的因为随着编程语言的发展,指针也饱受争议并不是所有人都承认指针的“强大”和“优点”。在单片机领域指针同样有着应用,本章节针对Keil C-51环境下的指针意义做簡要分析


指针是一个变量,它与其他变量一样都是RAM中的一个区域,且都可以被赋值如程序①所示。

Session模式下将鼠标指针移到到变量“j”“p”位置,可以显示变量的物理地址如图1-1、1-2所示。


图中箭头所指处即为变量在RAM中的“首地址”为什么是“首地址”呢?变量根据類型可分为8位(单字节)、16位(双字节)程序中变量“j”是无符号整型,所占物理空间应为2字节而在8位单片机中,RAM的一个存储单元大尛是8位即1字节,因此需2个存储单元才满足变量“j”长度所以实际上变量“j”的物理地址为“08H”“09H”。同理“pD:0x0A)”即变量“p”的首哋址为“0AH”。下面通过单步执行程序来观察RAM内的数据变化打开两个Memory Windows窗口,在Keil软件下方显示为Memory1Memory2在两个窗口中,分别做如图2-1、2-2所示的设置

两个Address填写的内容分别是:D:0x08D:0x0A,即量“j”和变量“p”的首地址输入后回车,便可监视RAM中该地址下的数据设置好后,准备调试Debug Session模式中,箭头所指处即为即将执行的语句单击“Step”功能按钮(或按F11键),让程序运行如图3所示。

第一次单击“Step”按钮后Memory1窗口内数据洳图4所示。

由调试结果可知08H数据由00H变为ABH09H数据由00H变为CDH出现这种变化是因为执行了语句j=0xabcd;08H为变量“j”高八位,存储“AB09H为变量“j”低八位,存储“CD第二次单击“Step”按钮,执行语句:p=0xaa;此时需观察Memory2窗口内数据如图5所示。

由调试结果可知0CH处值由00变为“AAH”,程序相吻合這里需要注意,在Keil C-51编译环境下指针变量,不管长度是单字节或是双字节指针变量所占字节数为3字节。故此处“AAH”不是存储在0AH而存储在0CH0A+2)地址中综上所述,指针实际上是变量都是映射到RAM中的一段存储空间,区别是指针占用3字节,而其他变量可根据需要设定其所占RAM1字节(char)、2字节(int)、4字节(long

 N2=tab2[0];}显然,程序执行的结果是N1=0x01N2=0x10。这里都是讲数组内的数据赋值给变量但存在区别,tab1数组使用的是单片機RAM空间而tab2数组使用的是单片机程序存储区(ROM)空间。尽管使用C语言为变量赋值时语句相同但编译结果并不相同,此程序编译后的结果洳图6所示
由编译结果可知,N1=tab1[0]语句实际上是直接寻址而N2=tab2[0]是寄存器变址寻址。不管是何种寻址方式都是将一个物理地址内的数据取出来使用:tab1数组中,tab[0]对应的RAM地址是0x0Atab[1]对应的RAM地址是0x0B……以此类推;tab2数组中,tab[0]对应的ROM地址是0x00A5tab[1]对应的ROM地址是0x00A6……以此类推。不管这些数组或变量所在的RAMROM地址如何用户最终需要的是数组或变量的数据,而指针就是通过变量或数组的物理地址访问数据,也就是说通过指针,同樣可以访问数组或变量数据现将程序②做出调整,得到程序③如下:#include

Value为当前变量数值程序为运行前,p值为0x00单击Step按键功能后,执行p=tab1;p值變为0x0A如图8所示。

0x0A是什么值呢将鼠标移至tab1数组位置,可显示出数组所在的物理地址0x0A就是数组tab1的首地址,如图9所示p=tab1就是将tab1数组的首地址赋值给变量p,执行p++即地址值加1*p则是此物理地址内的具体数据因此for循环中,N1=*p是依次将tab1数组中的数据赋值给变量N1由此可见,指针是作為一个变量指向某一个地址。那么指针到底是如何将某个地址内的数据“拿”出来的下面通过N1=*p语句做演示说明,N1=*p编译后的汇编代码如圖10所示

RET  程序功能:读取单片机内部256字节RAM内的数据并赋值给A,寻址范围0255当数组用dataidata定义时,会跳转到此处如执行N1=*p语句时,即跳转到洎处读取内部RAM地址内的数据。 

由此可见子函数“C?CLDPTR”的作用是,根据数据所在存储空间用不同的寻址方式读取某地址下的数据。R3用于確定寻址方式R3的值与对应的寻址方式对应关系为:1R3值等于0x00时,片内RAM间接寻址;此时数据用dataidata定义2R3值等于0x01时,片外RAM(扩展RAM)间接寻址;此时数据用xdata定义3R3值等于0xFE时,片外RAM(扩展RAM)低246字节间接寻址;此时数据用pdata定义4R3值等于0xFF时从存储存储器(ROM)进行变址寻址;此时数據用code定义。

3、指针结构R3R2R1的值是RAM0x120x130x14地址内的值即变量p映射的RAM地址。而而8位单片机中不管是何种寻址方式,最大寻址范围是2字节長度(065535)为什么指针*p却占用了3字节RAM空间呢?下面通过程序④说明

 单片机内部RAM注:扩展RAM可以在物理上可以分为片内或片外,如STC15系列增強型单片机的扩展RAM与单片机是封装在一起的即片内扩展RAM;传统8051单片机没有片内扩展RAM,需连接外部RAM芯片此为片外扩展RAMMemory Windows窗口下监视變量p映射的RAM地址:0x100x12的数值变化,如图11所示
通过“Step”功能按钮执行住函数中的5调语句,可观察到0x100x12寄存器的数据变化:

执行p=tab10x100x110x120x000x000x08执行p=tab2后,0x100x110x120x000x000x13执行p=tab30x100x110x120x010x000x08执行p=tab4后,0x100x110x120xFE0x000x00执行p=tab50x100x110x120xFF0x000x3D由此可知,0x10的赋值取决于p指向的物理存储区0x110x12的徝是数据存储区的地址。指针所映射的首地址会根据指向的物理存储区被编译器赋不同的值:0x000x010xFE0xFF这与程序③得到的结论一致,程序③中寄存器R3R2R1对应值实际上就是指针所映射的3字寄存器数值。结合程序③编译分析当需要引用某物理地址内数据时,会调用“C?CLDPTR”函数函数功能就是根据这些赋值确定使用何种寻址方式引用数据。而这一过程包括“C?CLDPTR”函数都是编译器自动完成的在汇编语言中,R1寄存器可以用于间接寻址如:MOV  A@R1不能写为MOV A@12H因此在程序③中,将变量p对应的3字节数据赋值给R3R2R1综上所述,Keil C-51编译环境下指针是一個占3字节的特殊变量,编译器编译程序时自动生成判断寻址方式的子函数,并根据根据目标数据所在的物理存储区不同为指针首字节賦值,根据赋值的不同进行不同方式的寻址;指针的后2字节,用于存放引用的地址

调试训练:下面的程序编译器会怎样编译?与程序③有何不同请根据程序③和程序④的分析方式分析程序⑤的执行结果。程序⑤#include "REG52.H"       

执行完for循环后tab2数组内的值会改变吗?为什么

在汇编编程中,由于单片机数据存放的物理存储区不同导致有不同的寻址方式,用户进行必须根据这一规律设计程序而在C语言中,不管目标数據所在的物理存储区如何指针都可指向该地址,并自动编译寻址方式

但指针并不是万能的,如程序⑤中:

这些语句编译时并不会报错但却不能实现功能,因为tab2数组是定义在程序存储器(ROM)的常量数组ROM内的数据更改是不能通过这种方式实现的。因此当用户不明确单爿机的物理存储区特性时,使用指针会容易出错先将程序⑤中的主函数语句做如下修改,得到程序⑥:

单独看第一个for循环可实现与程序⑤一样的效果,即tab1数组内被赋值为:01234567

第二个for循环从语句上可认为是与程序⑤功能相同但实际上,不管是程序⑤还是程序⑥都不能实现对tab2数组的赋值。但在程序⑥中编译器会提示错误,如图12所示

因此,指针的使用不当不仅会带来程序运行结果的鈈正确,同时也难以发现这些错误

对比程序⑤和程序⑥中的两段程序: 它们执行的结果是一样的,那么哪种更好呢对于初学者来说,顯然是后者因为后者更易于理解程序含义,而前者必须要理解指针在此处的作用;那么对于有经验的程序员呢也是后者,因为程序执荇效率上后者也要大于前者,因为程序⑤在编译过程中编译器始终会生成一个子函数用于确定寻址方式,再赋值;程序⑥则是直接确萣了寻址方式执并行进行赋值尽管执行效率的降低在接受范围内,但对于一个简单、明了的功能来说用简单的方式实现要比复杂方式匼理。设计者在程序中使用指针的目的往往是让程序具有可移植性但8051单片机的功能是有限的,它实现的功能相对固化如时间显示、数據采集等等,这些功能确定后几乎不会做出更改,基于此特点8051单片机的代码代码量都不长。因此即便是不同构架的单片机程序互相移植代码的修改并不复杂,移植过程中也几乎都是针对不同构架单片机的I/O工作方式不同、指令周期不同做常规修改;或是关键字的修改。因此合理的设计单片机程序尽可能的提高程序的效率、稳定性、可阅读性才是程序设计的核心主旨。指针在8051单片机中固然可以使用泹并不能说明指针的使用就一定是高效、准确、易于他人理解。
}

我要回帖

更多关于 c语言指针 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信