合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
【23.1 **减法溢出与假想借位**。】 英文“unsigned”的中文意思就是”无符号的”,延伸含义是“无负号无负数”的意思,所以unsigned char ,unsigned int ,unsigned long这三种类型数据都是无负号无负数的,取值只能是0和正数,那么问题来了,当被减数小于减数的时候,运算结果会是什么样子,有什么规律?这就是本节要研究的减法溢出。 第一个例子: unsigned char a; a=0-1; 分析: 左边的“保存变量”a的数据长度是1个字节8位,a=0-1可以看成是十六进制的a=0x00-0x01。由于0x00比0x01小,所以假想一下需要向高位借位,借位后成了a=0x100-0x01。所以a的最终结果是0xff(十进制是255),这个“假想一下需要向高位借位”的过程就是本节制造的新概念“假想借位”。根据“假想借位”这个规律,如果是b也是unsigned char 类型,那么b=2-5自然就相当于b=0x102-0x05,运算结果b等于0xfd(十进制是253)。 第二个例子: unsigned int c; c=0-1; 分析: 左边的“保存变量”c的数据长度是2个字节16位,c=0-1可以看成是十六进制的c=0x0000-0x0001。由于0x0000比0x0001小,所以假想一下需要向高位借位,借位后成了c=0x10000-0x0001。所以c的最终结果是0xffff(十进制是65535)。根据“假想借位”这个规律,如果是d也是unsigned int 类型,那么d=2-5自然就相当于d=0x10002-0x0005,运算结果d等于0xfffd(十进制是65533)。 综合分析: 为什么上述例子中会出现数据越减越大的奇葩现象?是因为减法溢出,是因为“假想借位”中的“借”是“光借不还”。一句话,根本问题就是溢出问题。 【23.2 因为**减法溢出,所以加减顺序......**】 第三个例子:请分析下面例子中e和f因加减运算顺序不同而引发什么问题。 unsigned char e; unsigned char f; e=1-6+7; f=1+7-6; 用两种思路分析: 第一种思路:只看过程不看结果。加减法的运算优先级是从左到右,e先减法后加法,1减去6就有溢出了,所以过程有问题。而f先加法后减法,整个过程没有问题。 第二种思路:先看结果再分析过程。e的运算结果居然是2,f的运算结果也是2。好奇怪,既然e的过程有问题,为什么运算结果却没有问题?其实e发生两次溢出,第一次是减法溢出,第二次是加法溢出,所以“溢溢得正”(这句话是开玩笑的)。1-6“假想借位”后相当于0x101-0x06,运算结果等于0xfb(十进制是251),然后0xfb再加上0x07等于0x102,因为e是unsigned char 类型只有1个字节,根据加法溢出的规律,最后只保留了低8位的一个字节0x02,所以运算结果就是十进制的2。 结论: 虽然e的运算结果侥幸是对的,但是其运算过程发生了溢出是有问题的,当运算式子更复杂一些,比如有不同类型的变量时,就有可能导致运算结果也出错。所以得出的结论是:在加减法运中,为了减少出现减法溢出的现象,建议先加法后减法。在后续章节讲到的乘除法运算中,为了减小运算带来的误差也建议大家先乘法后除法。 【23.3 例程练习和分析。】 现在我们编写一个程序来验证上面讲到的例子: 程序代码如下: /\*---C语言学习区域的开始。-----------------------------------------------\*/ void main() //主函数 { unsigned char a; //定义一个变量a,并且分配了1个字节的RAM空间。 unsigned char b; //定义一个变量b,并且分配了1个字节的RAM空间。 unsigned int c; //定义一个变量c,并且分配了2个字节的RAM空间。 unsigned int d; //定义一个变量d,并且分配了2个字节的RAM空间。 unsigned char e; //定义一个变量e,并且分配了1个字节的RAM空间。 unsigned char f; //定义一个变量f,并且分配了1个字节的RAM空间。 //第一个例子,针对a与b都是unsigned char类型数据。 a=0-1; b=2-5; //第二个例子,针对c与d都是unsigned int类型的数据。 c=0-1; d=2-5; //第三个例子,e与f的加减顺序不一样。 e=1-6+7; f=1+7-6; View(a); //把第1个数a发送到电脑端的串口助手软件上观察。 View(b); //把第2个数b发送到电脑端的串口助手软件上观察。 View(c); //把第3个数c发送到电脑端的串口助手软件上观察。 View(d); //把第4个数d发送到电脑端的串口助手软件上观察。 View(e); //把第5个数e发送到电脑端的串口助手软件上观察。 View(f); //把第6个数f发送到电脑端的串口助手软件上观察。 while(1) { } } /\*---C语言学习区域的结束。-----------------------------------------------\*/ 在电脑串口助手软件上观察到的程序执行现象如下: 开始... 第1个数 十进制:255 十六进制:FF 二进制:11111111 第2个数 十进制:253 十六进制:FD 二进制:11111101 第3个数 十进制:65535 十六进制:FFFF 二进制:1111111111111111 第4个数 十进制:65533 十六进制:FFFD 二进制:1111111111111101 第5个数 十进制:2 十六进制:2 二进制:10 第6个数 十进制:2 十六进制:2 二进制:10 分析: 通过实验结果,发现在单片机上的计算结果和我们的分析是一致的。 【23.4 如何在单片机上练习本章节C语言程序?】 直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。