指针在单片机C语言里面占有重用地位,但是指针也是单片机C语言里面可以说是最难以理解的一个点。C语言属于高级语言,但是指针却有汇编语言的特性,因此如果只讲指针,不讲硬件,那么听起来就会感觉再云里雾里。如果学过汇编,了解过单片机的底层结构,那么理解指针就容易得多,指针相当于汇编里面是直接寻址,寄存器间接寻址,但是比两种寻址会更加灵活和方便,无需进行寄存器选择和寄存器操作,因为已经进行了归一化/格式化处理。。
面下面我就用对房子进行类比(RAM空间),然后说一说我对指针的理解。
1.指针是什么
通俗一点讲指针就是内存空间的一个值,这个值存着地址(RAM空间或是ROM空间地址)。如果不说它是指针,那么和普通内存的数据没有什么区别。
只是在应用的时候会根据不同类型的指针加载在不同的寄存器里面,用于干不同的事情。
2.指针的作用
指针的作用就是寻址,当找到对应的地址了,就可以对地址空间相对应的内容进行处理。
3.指针本身宽度
首先,单片机体系决定了指针本身的的存储宽度,也就是说不管是什么类型(u8*,u16*,u32*,注意有些单片机是不支持u32*指针的)的指针,其宽度都一样。
因为指针本身就是内存里面的数据,只是这个数据用于表示数据空间/程序空间地址,或是说指向某个地址。在不同的单片机体系中,指针本身的宽度不一样,由单片机的架构决定本身宽度。
比如应广单片机指针,本身占用2个字节
比如51指针,本身占用3个字节,
比如M0指针,本身占用4个字节。
单片机的架构决定指针本身的数据宽度,那为啥还会有指针的类型(宽度)。此宽度非彼宽度,前者指的是指针本身宽度,占内存多少字节;后者指针类型(宽度)是指:指针指向的地址的数据类型的类型(宽度)
4.指针类型(指针指向的数据的宽度)
**指针类型:**指的指针指向目的地址的数据类型的宽度,也叫指针类型,比如有u8*,u16*,u32*。
u8*pucBuilding;/**/
u16*pusBuilding;
u32*puiBuilding;
比如 定义的是u8*,那么pucBuilding++; 实际地址空间就增加1byte
定义的是u16*,那么pusBuilding++; 实际地址空间就增加2byte
定义的是u32*,那么puiBuilding++; 实际地址空间就增加4byte
让使用指针的时候,针对不同的数据类型,能够方便快捷地找到对应地址,并进行对应的操作
为什么说指针难学,另外一个原因就是缺少明确的表述,相当于间接操作,要区分源和目的,还有类型。
5**.绝对地址指针(类比汇编直接寻址,C语言** 一般比较少这样用,但理解指针要从这里开始**)**
绝对地址指针可以指向内存空间的任意地址。
来个比喻,比如在楼栋里面,共8楼,每楼有16套房子,(8*16=128,其实这个房子就是内存空间)。你说你是208,人家就知道你是2楼8号
101->0x00
102->0x01
201->0x10
208->0x17
.......
508->0x57
可以用 u8 * pucBuilding=(*u8)0x17(绝对地址),C语言的意思就是,把这个0x17强制转换成地址,并使这个指针pucBuilding指向1byte宽度的数据空间0x17.相当于直接告诉房号,然后找房子。
只要定义了一个指针,就可以指向数据空间的任意地址,除了可以覆盖指向任意一个房号(101-816)外,其有能力指向不存在在空间,因为指针的宽度是由单片机体系结构决定的,也就是说指针本身可以表示一个很大的值,比如应广的指针式16bit宽度,可以表示的地址范围(0-0xffff),实际上内存空间可能只有64byt/80byte;
当*pucBuilding=(*u8)0x27, 对应就是3楼8号房
* pucBuilding=(*u8)0xF8,对应指向16楼8号房(实际不存在),指针有这个能力,但是不一定可用。
一旦确定内存空间范围(1-128),那么在使用指针的时候,就要对这个指向的范围加以限制,否则就可能出现系统异常,溢出或死机。
* pucBuilding=(*u8)0xF8中,pucBuilding使指向就是那空中楼阁,如果要对指针指向的数据进行操作,那么那么就出现溢出了,因为单片机只有128byt的RAM空间。
当然,如果只有128个内存空间,如果要是用u16 * pucBuilding=(*u16)0x0f,也没有关系,但是操作数据的时候就要注意,这个指针是按照16bit宽度进行数据操作,也就是两间房合并成一间房子,用同样一个编号。
不管使用的是任何类型指针,指针的操作,都一定要限制其操作范围,否则可能出现异常,溢出,甚至还会引起死机等问题。
在绝对寻址里面,其指向范围是可以整个内存空间,包括不存在的空间,这个就要注意了,要先了解内存结构,再用这个指针。
使用指针前,一是确定指针是范围,二是确定指针类型,两者缺一不可
指针的范围确认,指针才不会指向空中楼阁
指针的类型确认,指针一次性是操作几个字节
u8*指针眼里,内存是按照byte为单元顺序排列
u16*指针眼里,内存是按照2byte为一个单元顺序排列
u32*指针眼里,内存是按照4byte为一个单元顺序排列
6.相对地址指针(寄存器间接寻址,这个比较常用)
绝对指针可以指向数据空间的任意地方,给什么地址,就指向什么地址;
而相对指针是指向内存空间的某个区块,一般这个地址来自某个已经分配好内存空间的数据块所在的空间,比如数组,比如结构体。其限定范围是就是这个区块,属于间接得到这个地址。
比如定义了一个 u8 aucDat[8];/*如果不指定其绝对地址,那么这个数组可能存放在内存空间的任意位置*/
比如 aucDat[0]=0x23;
aucDat[1]=0x01;
u8 * pucBuilding=&aucDat[0];/*间接地址,不管这个数组存在任何数据空间的位置,只要知道其首地址,就能对其进行操作*/
意思是把这个数组的首地址赋给pucBuilding,
u8 dat=* pusBuilding;/*那么得到dat为0x23,*/
从这边就可以回答一个问题就是,为什么数组的标号不能当指针用,因为数组标号就只是一个标号,数组的标号并不占用内存空间,里面存的是数组的内容,因此无法当作指针用用。
那么通过pucBuilding指针就可以操作任何数组的任意一个值了.这个指针里面存的值就是数组的首地址
pucBuilding++;/*指向aucDat[1]*/
如果数组中存的4个是16位数据,相当于两个byte合成一个16bit数据。
其实也可以 定义u16 * pusBuilding=(u16 *)&aucDat[0];
这样通过指针,就可以按照16bit 数方式操作这个数组了。
u16 dat=* pusBuilding;/*dat=0x0123,假如单片机是小端模式*/
使用指针的好处就是灵活,相当于知道对方的门牌号码,找到之后就可以很方便进行对应的数据操作,指哪里打哪里,直接上门服务。也就是无需通过数据搬移传递,更新,然后再搬回;直接再在对应地址操作就行了。这样就可以大大减少数据传递和搬移时间,提高运算速度和效率,也减少内存空间的使用。
但是因为指针对数据操作的功能非常强大,如果不进行范围限制,那么可能有一定破坏性。任何强大的东西都有两面性,因此再次强调一点就是,对指针操作前一定要注意限制其范围,一定要再指定的范围内使用,防止指针越界。
7**.函数指针**
其实这个和数组指针是差不多,只不过指向的是程序空间地址。
总结
一,指针本身占内存空间,
二,这个内存空间存着地址,也可以说是指向一个地址
三,这个内存空间指向的地址同样存储着数据(可能也是地址喔,二级指针)
四,这些数据可以按照不同的数据类型来操作
五,通过指针操作这些数据的时候,要限定指针的操作范围。
只要掌握了指针原理,所有指针用法都几乎一样,就是找到对应要操作数据的内存/程序的位置,然后再进行对应的操作。再三强调一下,使用指针的时候必须限制其范围,否则会带来很多莫名奇怪的问题,这种问题往往非常隐秘,这种问题定位也很困难,也可能很久不出问题,但是往往一出问题就是致命问题。
总之, c语言指针就是把汇编的寻址方式归一化,方便大家直接操作地址空间所对应的值。希望大家学好指针,用好指针,可以指哪里打哪里,不用周转,不用拐弯,直接上门服务就完事了。希望大家喜欢,水平有限,欢迎指正。