9.5不同数据类型间的相互转换在C语言中,不同数据类型之间是可以混合运算的。当表达式中的数据类型不一致时,首先转换为同一种类型,然后再进行计算。C语言有两种方法实现类型转换,一是自动类型转换,另外一种是强制类型转换。 不同数据类型之间转换的主要原则是:短字节的数据向长字节数据转换。 比如:unsigned char a; unsigned int b; unsigned int c; c = a *b; 在运算的过程中,程序会自动全部按照unsigned int型来计算。比如a=10,b=200,c的结果就是2000。那当a=100,b=700,那c是70000吗?新手最容易犯这种错误,要注意每个变量类型的取值范围,c的数据类型是unsigned int型,取值范围是0~65535,而70000超过65535了,其结果会溢出,最终c的结果是(70000 - 65536) = 4464。 那要想让c正常获得70000这个结果,需要把c定义成一个unsigned long型。如果写成:unsigned char a=100; unsigned int b=700; unsigned long c=0; c = a*b;有做过实验的同学,会发现这个c的结果还是4464,这个是个什么情况呢? 请注意,C语言不同类型运算的时候数值会转换同一类型运算,但是每一步运算都会进行识别判断,不会进行一个总的分析判断。比如这段代码中a和b相乘的时候,是按照unsigned int类型运算的,运算的结果也是unsigned int类型的4464,只是最终把unsigned int类型4464赋值给了一个unsigned long型的变量而已。在运算的时候如何避免这类问题的产生呢?可以采用强制类型转换的方法。 在一个变量前边加上一个数据类型名,并且这个类型名用小括号括起来,就表示把这个变量强制转换成括号里的类型。如 c = (unsigned long)a * b;由于强制类型转换运算符优先级高于*,所以这个地方的运算是先把a转换成一个unsigned long型的变量,而后与b相乘,根据C语言的规则b会自动转换成一个unsigned long型的变量,而后运算完毕结果也是一个unsigned long型的,最终赋值给了c。 不同类型变量之间的相互赋值,短字节类型变量向长字节类型变量赋值时,其值保持不变,比如unsigned char a=100; unsigned int b=700; b=a;那么最终b的值就是100了。但是如果程序是unsigned char a=100; unsigned int b=700; a=b;那么a的值仅仅是取了b的低8位,首先要把700变成一个16位的二进制数据,然后取它的低8位出来,也就是188,这就是长字节类型给短字节类型赋值的结果,会从长字节类型的低位开始截取刚好等于短字节类型长度的位,然后赋给短字节类型。 在51单片机里边,有一种特殊情况,就是bit类型的变量,这个bit类型的强制类型转换,是不符合上边讲的这个原则的,比如bit a=0; unsigned char b; a=(bit)b;使用bit做强制类型转换,不是取b的最低位,而是它会判断b这个变量是0还是非0的值,如果b是0,那么a的结果就是0,如果b是任意非0的其它值,那么a的结果都是1。 9.6定时器中断响应调整在6.5.2章节有一个数码管秒表显示程序,那个程序是1秒数码管加1,但是细心的读者做了实验,经过长时间运行会发现,和实际的时间有了较大误差了,那如何去调整这种误差呢?要解决问题,先找到问题是什么原因造成的。 先对前面讲过的中断内容做一个较深层次的补充。还是讲解中断的那个场景,当在看电视的时候,突然发生了水开的中断,必须去提水的时候,第一,从电视跟前跑到厨房需要一定的时间,第二,因为看的电视是智能数字电视,因此在去提水之前可以使用遥控器将电视进行暂停操作,方便回来后继续从刚才的剧情往下进行。暂停电视,跑到厨房提水,这一点点时间是很短的,在实际生活中可以忽略不计,但是在单片机秒表程序中,误差是会累计的,每1秒钟都差了几个微秒,时间一久,造成的累计误差就不可小觑了。 单片机系统里,硬件进入中断需要一定的时间,大概是几个机器周期,还要进行原始数据保护,就是把进中断之前程序运行的一些变量先保存起来,专业术语叫做中断压栈,进入中断后,重新给定时器TH和TL赋值,也需要几个机器周期,这样下来就会消耗一定的时间,得把这些时间补偿回来。 方法一,使用软件debug进行补偿。 前边讲过使用debug来观察程序运行时间,那可以把2次进入中断的时间间隔观察出来,看看和实际定时的时间相差了几个机器周期,然后在进行定时器初值赋值的时候,进行一个调整。Kingst51开发板用的是11.0592M的晶振,发现差了几个机器周期,就把定时器初值加上几个机器周期,这样就相当于进行了一个补偿。 方法二,使用累计误差计算出来。 有的时候,除了程序本身存在的误差外,硬件精度也可能会影响到时钟的精度,比如晶振,会随着温度变化出现温漂现象,就是实际值和标称值要差一点。那么还可以采取累计误差的方法来提高精度。让时钟运行半个小时或者一个小时,看看最终时间差了几秒,然后算算一共进了多少次中断,把差的几秒平均分配到每次中断中,就可以实现时钟的调整。这个世界上本就没有绝对的精确,只能在一定程度上提高精确度,但是永远都不会使误差为零。 9.7字节操作修改位的技巧在编程时,有的情况下需要改变一个字节中的某一位或者几位,但是又不想改变其它位原有的值,该如何操作呢? 例如学习定时器的时候遇到一个寄存器TCON,这个寄存器是可以进行位操作的,可以直接写TR0=1;TR0是TCON的一个位。还有一个寄存器TMOD,这个寄存器是不支持位操作的,那如果要使用T0的模式1,希望达到的效果是TMOD的低4位是0b0001,但如果直接写成TMOD = 0x01的话,实际上已经同时操作到了高4位,即属于T1的部分,设置成了0b0000,如果T1定时器没有用到的话,那随便怎么样都行,但是如果程序中既用到了T0,又用到了T1,那设置T0的同时已经干扰到了T1的模式配置,这是不希望看到的结果。 在这种情况下,就可以用前边学过的“&”和“|”运算了。对于二进制位操作来说,不管该位原来的值是0还是1,它跟0进行&运算,得到的结果都是0,而跟1进行&运算,将保持原来的值不变;不管该位原来的值是0还是1,它跟1进行|运算,得到的结果都是1,而跟0进行|运算,将保持原来的值不变。 利用上述这个规律,就可以着手解决刚才的问题了。如果现在要设置TMOD使定时器0工作在模式1下,又不干扰定时器1的配置,可以进行这样的操作:TMOD = TMOD & 0xF0; TMOD = TMOD | 0x01;第一步与0xF0做&运算后,TMOD的高4位不变,低4位清零,变成了0bxxxx0000;然后再进行第二步与0x01进行|运算,那么高7位均不变,最低位变成1了,这样就完成了只将低4位的值修改位0b0001,而高4位保持原值不变的任务,即只设置了T0而不影响T1。 另外,在C语言中,a &= b;等价于a = a&b;同理,a |= b;等价于a = a|b;那么刚才的一段代码就可以写成TMOD &= 0xF0;TMOD |= 0x01这样的简写形式。这种写法可以一定程度上简化代码,是C语言常用的一种编程风格。
|