在计算机中,正如前文所述,数据类型本身存在一个缺陷——任何数据类型都存在一个数值范围,如果表示的数值超出这个范围,系统将产生“越界”错误,例如short int数据类型表示的范围是-32768 ~ 32767,当你用它来表示32768或者是-32769时,就将产生“越界”错误。其实、这里的“越界”只是虚拟中的假设,稳定的计算机是绝不允许这种事情发生,它总会通过各种方法将数据类型表示的数值“转化”到范围之内,例如用short int数据类型来表示32768时,计算机却自动的将其转化为-32768。这样、疑问就产生了,计算机是如何实现这种“转化”呢?这种“转化”又将会给程序带来什么呢?
从第二个问题开始:这种‘转化’又将会带来什么呢?
首先、我们来看如下一段代码。
#0001 /****************************************************************************** #0002 ** 文件名称:Chgdata.cpp #0003 ** 作 者:罗振辉 #0004 ** 完成日期:2009-9-24 #0005 ******************************************************************************/ #0006 #include<iostream> #0007 using namespace std; #0008 volatile short int ivalue = 32767; #0009 short int getAvalue(void) #0010 { #0011 return ivalue; #0012 } #0013 #0014 short int getBvalue(void) #0015 { #0016 return ivalue+1; #0017 } #0018 #0019 void testing(void) #0020 { #0021 for(int i=0;ivalue<40000;ivalue++); #0022 } #0023 int main(void) #0024 { #0025 cout<< "Avalue: " << getAvalue() <<endl; #0026 cout<< "Bvalue: " << getBvalue() <<endl; #0027 testing(); #0028 return 0; #0029 } |
运行结果如下;
#0030 Avalue: 32767 #0031 Bvalue: -32768 #0032 >>进入无限循环 |
从结果中可以发现,当ivalue增加1的时候,ivalue值却一跃变成的-32768,使得原本期望的32768一跃之间变成了-32768,同时也导致ivalue值永远小于40000,程序陷入无限循环状态,无法退出。
这样的例子其实还很多,比如在开发一套高校学生管理系统时,如果学校的人数不多时,到也不会出现什么问题,但是、当学校的学生人数很多(多于32767)时,如果开发者轻易的选择short int数据类型来实现学生信息遍历功能,那么、当系统运行时,该功能将因short int缺陷导致系统陷入异常状态,重则将导致系统瘫痪。
因此、建议:在程序设计的过程当中,特别对于大批数据信息处理时,因慎重考虑数据类型的选择,为提高程序的复用性、可靠性和易适用性等特性,数据类型应根据实际情况尽量选择数值区间较大的那种。
第一个问题:计算机如何实现这种“转化”呢?
计算机是如何自动将short int类型数据表示的32768转化为-32768呢?这是一个比较棘手的问题,需要对计算机内部数据表示方法有所了解。然而、不仅要问,何为计算机内部数据表示方法?真实的数据又是如何和计算机打交道的呢?通过下面几节的阐述,你将清楚的明白其中道理。
1. 二进制发展概述
在早期设计的机械计算装置中,使用的不是二进制,而是十进制或者其他进制,利用齿轮的不同位置表示不同的数值,这种计算装置可能更加接近人类的思想方式。比如说一个计算设备有十个齿轮,它们级连起来,每一个齿轮有十格,小齿轮转一圈大齿轮走一格。这就是一个简单的十位十进制的数据表示设备了,可以表示0到999999999的数字。配合其他的一些机械设备,这样一个简单的基于齿轮的装置就可以实现简单的十进制加减法了。这种通过不同的位置上面不同的符号表示数值的方法就是进制表示方法。
电子计算机出现以后,使用电子管来表示十种状态过于复杂,所以所有的电子计算机中只有两种基本的状态,开和关。也就是说,电子管的两种状态决定了以电子管为基础的电子计算机采用二进制来表示数字和数据。
微计算机出现以后,计算机的处理能力和可编程能力越来越强,历经数十年的发展,二进制理论也逐步成熟,这使得它在计算机中运用的越来越广泛,一跃成为真正和计算机硬件的唯一语言。同时、为简化运算规则,二进制引进了原码、反码、补码概念,并规定:有符号数据的最高位为符号位,符号位为1表示负数,为0表示正数。随后、在大多数数据运算的过程当中,真正与计算机打交道就是二进制数的补码。但由于用二进制表示一个数时,位数多等原因,在实际使用中多采用送入数字系统前用十进制,送入机器后再转换成二进制数,让数字系统进行运算,运算结束后再将二进制转换为十进制供人们阅读,这样从而提高了计算机的可用性。经过这个阶段的发展后,二进制从理论上还从实际应用等方面都得到一个质的提升,二进制理论也就堂而皇之进入了大学的课堂。
2. 二进制运算操作
正如前几节所述,二进制运算操作非常简单,与十进制数的运算十分相似。它的基数0、1,进位规则是“逢二进一”,借位规则是“借一当二”。最常用的是加法、减法、乘法、除法运算。
二进制加法
0+0=0、0+1=1、1+0=1 、1+1=10 四种情况,例如、求(1011)2+(1000)2的和。
1 0 1 1
+ 1 0 0 0
结果和为:1 0 0 1 1
二进制减法
0-0=0,1-0=1,1-1=0,10-1=1四种情况,其算法规则与加法运算相识。
二进制乘法
0×0=0、1×0=0、0×1=0、1×1=1四种情况,例如、求(1011)2乘(1000)2的积。
1 0 1 1
× 1 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
1 0 1 1
结果积为:1 0 1 1 0 0 0
二进制除法
0÷1=0,1÷1=1两种情况,例如求(1011)2除(1000)2的商。
1 0 1 1
÷ 1 0 0 0
1 <— 0 0 1 1 够除进1
1.00 <— 1 1 0 0 不够除向右借两位
1.01 <— 0 1 0 0 够除进1
1.010 <— 1 0 0 0 不够除向右借一位
1.011 <— 0 0 0 0 够整除进1
结果商为:1.0 1 1
3. 计算机为何选择补码作为数值的表示方式呢?
正如前几节所述,为简化运算规则,二进制引进了原码、反码、补码概念,并规定:有符号数据的最高位为符号位,符号位为1表示负数,为0表示正数。为回答这个问题,我们不得不从原码、反码、补码开始阐述。
原码、反码、补码——数值的表示方式
原码:一个数值,按照绝对值大小转换成的二进制数就称之为这个数的原码,例如-8的二进制原码是(10001000)2。
反码:正数不变,将负数(负二进制数)符号位不变、按位取反,所得的新二进制数称为这个二进制数的反码,例如(10001000)2的反码为(11110111)2。
补码:将二进制数的反码加1称为这个二进制数的补码,例如(10001000)2的补码为(11111000)2=[(11110111)2+1]。
原码形式运算弊端
有了数值的表示方法就可以对数进行算术运算,但是很快就发现用带符号位的原码进行乘除运算时结果正确,而在加减运算的时候就出现了问题,如下例子。
例、求1 +(- 1)和的结果,假设存储器以字节为单位,8位为一字节。
1的原码形式:(00000001)2 ;
-1的原码形式:(10000001)2 ;
那么、1 +(- 1)= (00000001)2 + (10000001)2 = (10000010)2 = -2;结果出错!
反码却也不是救世主!
经上述分析发现,原码在乘除、正数加减运算时结果是正确的,问题归根到负数运算上。反码如何解决这个问题的呢?如下例子。
例、求1+(-2)和的结果,假设存储器以字节为单位,8位为一字节。
1的反码形式:(00000001)2 ;
-2的反码形式:(11111101)2 ;
那么、[1+(-2)]反 = (00000001)2 + (11111101)2 = (111111110)2 ;
1+(-2)= [(111111110)2]反 = (10000001)2 = -1;结果正确!
可以得出结论:反码运算可以有效的避免了符号位的影响,将减法运算转化为加法运算,再利用“反码的反码等于原码”原理将反码运算的结果转化为最终真是的结果。
然而、反码虽然能解决大部分的问题,但也不是救世主!我们通过如下例子来进行分析吧。
例、求1 +(-1)和的结果,,假设存储器以字节为单位,8位为一字节。
1的反码形式:(00000001)2
-1的反码形式:(11111110)2
那么、[1 +(