算术型数据的溢出

来源:互联网 发布:雕爷牛腩知乎 编辑:程序博客网 时间:2024/04/29 20:28

         对于C系语言,在使用变量前需要显示声明变量的类型,这样在编译时期就可以执行类型检查,这样的语言称为静态类型语言;变量声明后除非进行显示类型转换,否则变量类型保持不变,这样的语言称为强类型语言。例如,PHP语言使用变量前无须声明,并且没有类型,同一个变量可以多次赋值成不同类型的数据。然而如此分类其实没什么意义,似乎并没有严格的定义来区分静态、动态类型语言和强、弱类型语言。C++的RTTI使其并不是“纯粹”的静态类型语言;允许隐式类型转换令部分人将C/C++划分为弱类型语言。对动静和强弱的划分应该是根据结果即会或不会引起某些行为来定义的。我个人更偏向于将使用变量前即须定义变量类型的语言称为静态类型语言;有明确类型系统的语言称为强类型语言。从这个角度看,C系语言都可以称为静态、强类型语言。当然,这只是一个很粗糙的、感性的认识。

  静态类型语言声明变量时即确定变量的类型,就会预先分配存储空间,这样在动态运行期间,如果某个变量存储的值超过了由其存储空间和表示方法决定的一个范围后,就会发生所谓的溢出。例如,一个int型的数据显然不能存储无限大的数,若一个数足够大,就无法存储进一个int型变量里。在C/C++语言里,对于数值溢出的默认行为是不处理,也就是说,需要程序员自己处理溢出。那么,该如何检测溢出呢?

       数的表示范围牵涉到编码,计算机中的数值整型数据大多是以补码表示,浮点型数据以原码和补码综合表示,这在组成原理等相关书籍上有详细讲解,比较繁琐,我也没深入学习,不想多说。如果CPU在硬件上对溢出有所处理,例如在溢出时设置溢出位,那可以通过汇编等语言检查溢出位来判断溢出;如果硬件不支持或使用高级语言,则常常是通过一些数值上的方法来判断溢出。例如两个正数相加结果却变成了负数,就可以判断是溢出了。这个方法不够通用,下面介绍一些通用的办法。

        简单且有效的办法是用更大的数据类型来存储数据,然后通过判断数值是否超过原始数据类型所能表示的最大值来判断是否溢出。例如,想判断两个整形变量的和或积作为int变量存储时是否会溢出,则令 long long n = a*b; 若 n > INT_MAX,则可判断其溢出了。这种方法似乎有取巧之嫌,也未必总是能找到更大的数据类型,但确实简单高效。

       再者,可以在做完运算后,用所得结果与原数据作逆运算,看所得结果是否与原数据一致,来判断是否溢出。例如 int c = a+b;若 c-a != b;则可判定是溢出了。

       还有,可以在运算之前判断是否会溢出,而不必等到溢出后再来判断,因为当前的计算机系统在硬件和语言层面上常常没有自动检测溢出的机制,存储单元并不对其所存储的二进制码作出限制,其含义由CPU和相应的系统负责解释。例如,0xF0 作为有符号数时其表示负数,作为无符号数时就是正数,存储器只负责存储,不知道这些二进制码的含义,其含义取决于如何解释;所以一旦溢出了,变量的存储单元已经存进了某个值,在不联系上下文的情况下,单单只看这个存储单元的值,是没有办法知道是否溢出的。因此要尽量在溢出前检测到是否会发生溢出,例如,可以这么写

if( b > INT_MAX-a)

则很明显,a+b > INT_MAX,在溢出之前就可以检测到溢出。

       另外提醒一下,不知道是幸运还是不幸,C/C++标准并不对数据类型所占字节数有强制规定,仅仅规定更大的数据类型所占字节数不小于较小的数据类型。这样,某种数据类型的数值表示范围是与具体平台相关的,这样做的目的也许是给编译器实现更大的自由以及提供扩展的灵活性。但如此一来,我们就不能事先知道某种数据类型的准确表示范围,因此,诸如int类型数据的所能表示的最大值这类问题通常都是由相应语言的库来提供的,例如INT_MAX就是C语言在<limits.h>中提供的int类型的最大值。

        溢出是现实世界中的数学计算由计算机编程实现时必须考虑的问题,面试时常常会考察到边界条件包括溢出判断。感觉这是个比较细节和繁琐的问题。

0 0
原创粉丝点击