1.字长的测试至于各种不同的数据类型在不同的系统中所占用的字节数,则可以使用sizeof()关键字(注意:初学者往往误认为函数,它是31个关键字中的一个)进行测试,顾名思义sizeof就是“size of…返回操作数的类型的长度”,以字节为单位。其一般的表示形式为:
sizeof(数据类型)
sizeof(变量)
比如:
sizeof(int) = 4;
也就是说,其返回int变量的字节数为4。
(题外话:明明所有的教材上都说C语言一共有32个关键字,为什么说是31个呢?那么,从今天开始,请记住:C语言只有31个有效的关键。如果那个再说32个,那肯定是脑子进水了。因为auto是从C语言的无类型前身B语言继承下来的,由于B语言没有类似int这样的关键字,因此声明必须包含存储类型,而事实上auto已经毫无用途。如果那个老师再出这个题,而你考试恰好差一分不及格,请将这段话告诉他们,让他们给你60分。当然,这段话不会写在教材上,权当大家看博客累了,开个玩笑而已,不要当真!)
2.负数的表示方法当需要带符号的signed整数时,虽然可以单独指定某位为1来表示“符号”,但计算机不会简单地将“符号位”加到一个数上去,这样反而会使计算机的运算器的设计更加复杂。那么负数在计算机中是如何存储的呢?规定最高位为符号位,也就是说,其最高有效位的数字具有不同的“权值”,即当其最高有效位为0时,其权值为2n-1,否则其权值为-2n-1。比如,当一个8位二进制数10110111被解释为一个无符号数数时,那么其十进制数的多项式求值结果为:
(10110111)2 = 1×(27)+1×(25)+1×(24) +1×(22) +1×(21) +1×(20) = (183)10
如果解释为一个带符号数时,则其十进制数的多项式求值结果为:
(10110111)2 = 1×(-27)+1×(25)+1×(24) +1×(22) +1×(21) +1×(20) = (-73)10
当约定用最高有效位作为符号位来确定singned整数之后,这样就可以使用“补码”将符号位和其它位统一处理,那么减法也就可以作为加法来处理了。其规则如下:
一个n位二进制原码为N,它的补码可定义为(N)补=2n-N。
当两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位位被舍弃。正数的补码与其原码一样,而负数的补码,其符号位为1,最简单的算法就是取反该数绝对值原码的所有位,然后将结果加1。由此可见,在计算机中数值一律用补码来表示(存储)。
3.溢出我们知道,一个n位二进制数用于表示unsigned数时,其可能的取值范围为0~2n-1,比如,一个8位数的范围在0~28-1(0~255)之间。假设在一个8位unsigned数248上加10,那么需要9位才能存储正确的结果258,而正确结果258与实际结果2(最低8位有效位)之间的差(256)对应的第9位被丢弃了,因此它不能存储在结果中。
当用于表示signed数时,则只有一半用于正值,一半用于负值,因此2的补码带符号数的整个取值范围为-2n~2n-1,即一个8位二进制数可以保存一个范围在-27~27-1(-128~127)之间的带符号值。因此对于signed数,当加上具有不同符号的数或减去具有相同符号的数时,将永远不会溢出。如果将两个具有相同符号的整数相加,或将两个具有不同符号的整数相减时,将可能发生溢出。比如,从一个8位signed整数-120中减去20,即:
((-120) + (-20))10 = (10001000 + 11101000)2 = (01110100)2 = (116)10
而其正确的结果-140需要9位才能存储,因此正确结果-140与实际结果116(最低8位有效位)之间的差(256)对应的第9位被丢弃了,它不能存储在结果中。
由于整数数据类型的溢出是悄悄发生的,因此一定要注意每个变量可能的取值范围。
4.整数的安全性2004年12月25日,星期六,Comair航空公司被迫停止了所有的业务,1100个航班无法起飞。这起事故源于Comair所使用的机组调度软件中的一个16位计数器,在任何月份,它最多只能表示32768次改变。而该月早期频繁的风暴导致过多的机组重新调配,从而使该16位计数器溢出,这起整数漏洞完全是因为整数表示的局限性所引起的。
其实,所有的整数漏洞都是由整型范围错误所引起的,比如,发生整数溢出是因为在整数操作时产生了超过特定整型表示范围的数值所致;发生符号错误是因为负数被存放在无法容纳该值范围的带符号类型中;而发生截断错误则是因为结果被存放在一个过小的类型中。由于整数漏洞都是类型范围错误,所以只要适当地应用类型范围检查,即可消除所有的整型漏洞。其主要措施有范围检查、强制类型转换、编译器运行时检查、安全的整数操作、任意精度的算术、测试与源代码审查,其实际意义上的漏洞防范将结合后续相关的示例另行说明。
简而言之,可利用的软件漏洞都是由本可避免的软件缺陷所引起的,因此只要揭示最可能导致安全缺陷的编程错误,从一开始就将安全性建立于系统之内,而不是将其作为补救措施附加进来。如何构建高质量的系统,让软件具有更少的漏洞,不易遭受代价高昂或后果严重的攻击,这是每一个程序员必须认真面对的根本问题。 |