抓虫记(1)

近日浏览CSDN时,看到了网友贴的一个关于百度面试题的帖子,其中有一道是改错题,实际上就是找出代码中可能存在的BUGs,这个题是这样的:

下面这段代码是把中英文混合字符串(汉字用两个字节表示,特点是第一个字节的最高位为1)中的大写字母转化为小写字母,请找出其中的bug,注意各种异常情况。

for (char *piterator = szWord; *piterator != 0; piterator++

    if (*piterator & 0x80 != 0
) { 
        piterator
++

    }
else if (*piterator >= 'A' && *piterator <= 'Z'
) { 
        *piterator += 32
    }
}
 

对于这个问题,我首先分析一下题目本身:
1.算法的基本思路比较清晰:遍历整个字符串,找出其中的大写的英文字符,然后把这些大写的英文字符转化成小写字符。
2.当字符串中某个字节的最高位是1,则这个字节和后面紧接着的一个字节组成了一个汉字字符。
3.对于字符串中占两个字节的汉字字符,我们应该跳过这个汉字字符,继续搜寻后面的字符。

有了这样的一个基本认识,我接着又考虑了一些细节的问题:
Q1:对于这样的字符串,我们用什么数据类型来保存其中的每个字节?
我们知道,ASCII编码的字符可以用signed char来保存,因为ASCII编码的字符的取值范围在[0,127],正好和signed char所能表示的正整数吻合。如果超出了这个范围,signed char就会溢出。本题的字符串中包含大于这个范围的值(字节的最高位是1),所以用signed char就不合适了,这时候就应该考虑unsigned char。

Q2:对于这样的字符串,我们用什么方式来表示字符串的结束?
我们知道,对于用ASCII编码的字符串,我们用'/0'字符来表示字符串的结束。对于本题中的字符串,该怎样表示字符串的结束?我也不能确定,暂时还用'/0'来表示吧。
 
Q3:对于汉字字符,它占两个字节,第一个字节的最高位是1,那第二个字节有什么特征?
从题目中看不出来有什么特征。所体它可能是'/0',也可能是ACSII编码的字符,也可能是其他字符。

好了,有了以上理论上的分析,我在原来代码的基础上写一个修改的版本:

for (unsigned char *piterator = szWord; *piterator != '/0'; piterator++)
{
    if ((*piterator >= 'A') && (*piterator <= 'Z')) {
        //if the character is uppercase character
        *piterator += 32;
    }else if ((*piterator & 0x80)){
        //if the character is the first character of chinese character
        if (*(piterator+1) == '/0') {
            //if the second character is null character
        }else {

            ++piterator;
        }
    }
}


代码的分析:
1.我使用unsigned char来替换char。
2.对于汉字字符,我们只知道它的第一个字符的特征,我们却不知道第二个字符的特征,这样就存在一些特殊的情况:
  2.1 汉字的第二个字符是'/0'。我们有可能把这个字符看作是整个字符串的结束字符,也就是字符串的最后的一个字符。这时候如果我们试图跳过这个字符去获取它后面的字符,就会发生字符串的"越界"错误。为了避免这样的错误,如果汉字的第二个字符是'/0',就不跳过这个字符。
  2.2 汉字的第二个字符是英文的大写字母。在原先的代码中如果检测到某个字符是汉字字符的第一个字符,会将指针调整到这个汉字字符的第二个字节,接着又判断这个字节是否是大写字母,这时候就有可能将这个字符看作是大写字母,这样就发生了错误。为了避免这样的错误,我将判断是否是大小字母的代码放到前面。
3.另外的一个错误就是,由于"!="的优先级大于"&"的优先级,原先的代码:

*piterator & 0x80 != 0

被编译器解析成:

(*piterator) & (0x80 != 0)

这样出来的结果就是错误的。为了避免这样的错误,我删除了"不等于0"的比较。

上面是我对这个问题的理解和分析,不可避免的也会存在一些问题,希望大家留言帮我指出来

09/24/2006 于 家中

修订v1.0


  在热情的回复中,网友blue_zyb指出了文中的一个错误,在此对blue_zyb表示感谢。突然意识到我在学习C语言的初期(大二,学校开了这门课),学习的时候太过粗略和马虎,导致到现在对于最基本的问题都弄的不是太清楚。
  对于这样的流程控制语句:

if  (condition1) {
  //clause1
}
else
 (condition2) {
  //clause2
}

如果condition1满足条件的话,就会执行clause1中的代码;如果condition1不满足条件的话,就会执行clause2中的代码,无论clause1还是clause2被执行,执行结束后程序都会跳出这个流程。也就是说,这里是一个"2选1"的流程。
  对于带有else if这样的流程控制语句:

if  (condition1) {
  //clause1
}
else if
(condition2) {
  //clause2
}
else if
(condition3) {
  //clause3
}else if (...) {
  ...
} else  {
  //clauseN
}

如果condition1满足条件的话,就会执行clause1中的代码;如果condition1不满足条件的话,就会接着判断condition2是否满足,如果condition2满足的话,就会执行clause2中的代码,否则的话就会判断clause3,与此类推,如果没有任何条件被满足的话。clauseN会被执行。任何一个clause被执行以后,程序都会跳出这个流程,不会再去评估其他的条件。也就是说,这里是一个"N选1"的过程。

  突然想到我在实际工作中就遇到这样的情况。我在浏览项目代码中看到一段类似这样的代码:

if (foobar() > 0 ) {
   
//clause1

}else if (1 ==  foobar()) {
   
//clause2

}

  在测试的时候,我发现当foobar()返回是1的时候,clause2却永远不会被执行,我当时就意识到这是一个逻辑错误,这时候clause1被执行,而不是clause2。可能是受到了这个的影响, 我在修改本文中的的BUG时潜意识的认为这个BUG有逻辑错误,却"画蛇添足"了一把。看来写代码真不是一件容易的事情,"革命还未成功,同志还需努力"

10/14/2006 于家中

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值