话说,C语言中最精髓的就是指针的操作了。当然,本文不对C语言的指针做详细的描述。Hanny在这里只想小结一下KeilC中的指针类型。
在KeilC中,我们会常见到下述的指针定义方式:
char *ptr1;<br />
char code *ptr2;<br />
char data *ptr3;<br />
char xdata *ptr4;<br />
char *data ptr5;<br />
char code *data ptr6;<br />
char data *data ptr7;<br />
char xdata *data ptr8;<br />
char *xdata ptr9;<br />
char code *xdata ptr10;<br />
char data *xdata ptr11;<br />
char xdata *xdata ptr12;
看到这里,可能会有人有点乱了吧。其实要理清上面这一堆东西很简单。
首先,我们需要了解51的内存结构。简单地说,就是51包括data/idata、xdata、code三大块。定义指针时,*后面的修饰符是用来表示指针的存储位置。如:ptr5、ptr6、ptr7、ptr8存储在data区中,ptr9、ptr10、ptr11、ptr12存储在xdata中。ptr1、ptr2、ptr3、ptr4的存储位置则取决于KeilC中的设置(使用内存的模式)。
*之前的修饰符,则是用来表示指针所指向的数据的存储位置,即指针类型。ptr2、ptr6、ptr10的指针,指向一个存储在code中的数据。ptr3、ptr7、ptr11的指针,指向一个存储在data中的数据。ptr4、ptr8、ptr12的指针,指向一个存储在xdata中的数据。
ptr1、ptr5、ptr9这三个指针有一点点特殊。没有修饰类型这表示这是一个通用指针,它将用3Byte来表示。其中第一字节表示指针类型,后两个指针存放指针地址。而类型中,0xff表示指针存放在code中,0x00表示data/idata,0x01表示xdata。
综上所述,若一个指针为char xdata * xdata,如ptr12,则该指针表示一个存放在xdata中的指针,并且该指针指向一个xdata的数据。
在使用过程中,只要注意到了这些类型,应该是不会出什么问题的。
在KeilC的选项中,C51这一项里有一项叫做“Don't use absolute register accesses”。我们已经因为这一项出了三次Bug了。
那么这一项有什么作用呢?
在KeilC默认中,这一项并没有选上,表示KeilC将使用寄存器的绝对定位来优化程序。什么意思呢?我们知道,51中有四组通用寄存器。在寄存器组0的时候,R0-R7实际上就映射到了0x01-0x07这段地址。也就是说有时候为了优化,KeilC将会使用0x01-0x07这样的绝对地址。举个简单的例子吧,比如说我们需要将寄存器R5的值存入R7,正常我们会这样写:
MOV A, R5
MOV R7, A
但是如果使用寄存器的绝对定位呢?我们就可以这样写了:
MOV R7, 0x05
节省了一条指令的执行时间!
当然,正常时候这样使用当然没有任何问题。然而,如果有一个中断的服务程序,我们使用了寄存器组1呢?这时候,R5就不再是0x05了,而是0x0D!因此,Bug就出来了。
当然,我们可以在这个中断服务程序上所调用的所有子程序中加在using 1的关键字来解决这个问题。不过这样还是带来了一些不便,更有一些时候,一些子函数是多处调用了。因此,有时我们一般都直接勾选了这个选项来取消KeilC的这项优化。
在C51中,KeilC与IAR在函数参数的传递及返回值的处理上,有着较大的不同。本文主要小结不同,并稍稍发表Hanny个人对这些方式的优缺点的看法。
首先,我们对数据类型进行分类。根据数据类型的长度,我们可以将数据简单分为:bit, u8, u16, u32。其中,bit表示位变量;u8主要为char、signed char、unsigned char型变量;u16主要为short、usigned short、int、unsigned int、point型变量;u32主要为long、unsigned long、float型变量。
然后,先介绍一下函数参数的传入。
首先是bit,KeilC采用的是位寻址区的变量来进行bit型参数的传入。而IAR采用的是B寄存器来传入。
接着是u8,KeilC主要采用R7、R5、R3来进行参数的传入。也就是说:当函数的参数为一个时使用R7,两个时使用R7和R5,三个时就使用R7、R5及R3。同样的,IAR主要采用R1、R2、R3、R4、R5来进行u8型参数的传递。
紧接着是u16,KeilC主要采用R6:R7、R4:R5、R2:R3来进行传入,而IAR采用R3:R2、R5:R4来进行传入。
再接着就是u32,KeilC主要采用R4:R7来进行传入,IAR采用R5:R2来进行传入。
最后,KeilC还支持一种叫做通用数据指针的数据类型。主要采用R2:R1、R3来传入。其中,R2:R1存储指针地址,R3存储内存类型。
然后再说说返回值。
返回值为bit时,KeilC与IAR都用C来传出。
返回值为u8时,Keil采用R7,而IAR采用R1。
返回值为u16时,Keil采用R6:R7,而IAR采用R3:R2。
返回值为u32时,Keil采用R4:R7,而IAR采用R5:R2。
下面是Hanny的个人小结:
KeilC的函数传入传出都是秉承KeilC的大端思想,主要用到的寄存器为R7-R2;IAR的函数传入传出是秉承小端思想,主要用到的寄存器为R1-R5。
KeilC函数参数传递的优点是:在从u8向u16、u32扩展时,由于低位所处的寄存器位置不变,扩展时对代码的修改较小。
IAR的优点是:在传入的参数位数为u8时,更紧凑的方式能够传入更多的参数。
相比之下,Hanny更喜欢KeilC这种方式。