- 积分
- 436
- 下载分
- 分
- 威望
- 点
- 原创币
- 点
- 下载
- 次
- 上传
- 次
- 注册时间
- 2008-11-27
- 精华
|
5#
发表于 2009-12-2 14:16:04
|
只看该作者
来自: 江西南昌 来自 江西南昌
要真正讲清楚,估计先学会绕口令,真得难讲清呀!只能凭着当年的回忆依稀来说说,当然也不一定正确。C语言里,是否溢出可以判断它是否小于原值。如果结果较大,最好用容量大一点的来判断它是否超出你要的范围。数据上溢或下溢,都有这样一些代码,表面看起来很正确。但是由于存在着微妙的问题,执行就会失败,也是严重的错误。“简单字符”就是这种性质的错误。下面的代码也具有这样的错误。C语言对整数是不会检测溢出的,它只是简单把超出部分"切掉",其结果在一般的情况下一个符号整数是否溢出可以判断它是否小于0 ,无符号整数。
char chToLower[ UCHAR_MAX+1 ];
void BuildToLowerTable( void ) /* ASCII版本*/
{
unsigned char ch;
/* 首先将每个字符置为它自己 */
for (ch=0; ch <= UCHAR_MAX;ch++)
chToLower[ch] = ch;
/* 现将小写字母放进大写字母的槽子里 */
for( ch = ‘A’; ch <= ‘Z’; ch++ )
chToLower[ch] = ch +’a’ – ‘A’;
}
……
#define tolower(ch)(chToLower[(unsigned char)(ch)])
尽管代码看上去很可靠,实际上编程很可能使系统挂起来。看一下第一个循环,什么时候ch大于UCHAR_MAX呢?如果你认为“从来也不会”,那就对了。如果你不这样认为,请看下面的释。假设ch等于UCHAR_MAX,那么循环语句理应执行最后一次了。但是就在最后测试之前,ch增加为UCHAR_MAX+1,这将引起ch上溢为0。因此,ch将总是小于等于UCHAR_MAX,机器将进行无限的循环。通过查看代码,这个问题很明显。变量也可能下溢,那将会造成同样的困境。下面是实现memchr函数的一段代码。它的功能是通过查寻存储块,来找到第一次出现的某个字符。如果在存储块中找到了该字符,则返问指向该字符的指针,否则,返回空指针。象上面的BuildToLowerTable一样,memchr的代码看上去似乎是正确的,实际上却是错误的。
void * memchr( void *pv, unsigned char ch, size_t size )
{
unsigned char *pch = (unsigned char *) pv;
while( -- size >=0 )
{
if( *pch == ch )
return (pch );
pch++;
}
return( NULL );
}
循环什么时候终止?只有当size小于0时,循环才会终止。可是size不会小于0,因为size是无符号值,当它为0时,表达式--size将使其下溢而成为类型size_t定义的最大无符号位这种下溢错误BuldToLowerTable中的错误更严重。假如,memchr在存储块中找到了字符,它将正确地工作,即使没有找到字符,它也不致使系统悬挂起来.而坚持查下去,直到在某处找到了这个字符并返回指向该字符的指针为止。然而,在某些应用中也可能产生非常严重的错误。我们希望编译程序能对“简单字符”错误和上面两种错误发出警告。但是几乎没有任何编译程序对这些问题给出警告。因此,在编译程序的销售商说有更好的编译代码生成器之前,程序员将依靠自已来发现上溢和下溢错误。但是,如果用户按照建议逐条跟踪代码,那么这三种错误就都能发现。用户将会发现,*pch在与0xff比较之前已经转换为0xffff,ch上溢为0,size下溢为0xffff。由于这些错误太微妙,用户可能花几小时仔细阅读代码,也不会发现上溢错,但是如果查看在调试状态下该程序的数据流,就能很容易地发现这些错误。 |
|