C陷阱篇之语法正确语义错误的编译器局限

    编译器功能只是语法检查,只要语法正确,那它就遵循一个原则:程序员总是对的。其实也只能这样,如果脑子里想着A,实现的却是B,而A/B语法上都成立,那编译器除了认为你正确,还能做什么呢?只能我们自己注意区分A/B相似且语法都成立的下列情况。

代码布局与缩进的误导

    计算机从不受代码语法和布局影响,而人却易受眼睛影响做出倾向性判断,这些判断有时是错误的。如:

    for (i=0; i<max_time; i++)

        LeftElmt = Left[i]; 

        Left[i] = Right[i];   

        Right[i] = LeftElmt;

    这种排列方式容易使人认为每次循环3条语句全部执行,实际上没有括号,只有第1条语句执行max_time次,而后两条语句仅执行一次。这里for循环与缩进放在一起,对外传递了误导信号,使人认为三条语句都被循环,而实际编译器却另有解释,结果出乎意料。所以类似情况不要省略{}

语句结束分号的匹配

    C程序中分号是语句结束标志,使用频率最高。常在河边走,哪能不湿鞋,一时手误多写或漏写;号,有时会迷惑编译器,导致Bug。比如:

    if (a > b);   //多加了一个;

      b = a;

    这段新代码仍然通顺,编译器检查不出语法错误,因为它相当于:

    if (a > b) { }

    b = a;

但这样的笔误会导致结果和期望的大不相同,对外表现就是BUG;号多了不行,少了同样有麻烦,如:

    if (a < 6)

       return     //少了;

    b = a;

    这里return后漏了结束分号,程序能通过编译,只是把b = a;当作return操作数,变成:

    if (a<6)

    return b = a;

    这样当a >=6时,预想的b=a;会被跳过,因为它和return一起成为a<6的条件执行部分。

    还要注意结构体/枚举/类等结尾的;符,if/for/while/switch等模块体紧跟的{}后面都不需要;号,而struct/enum/class定义后必须有,如果遗漏就会导致语意错误,如:

struct pic{

      ……

}

test(){….}

    struct结尾与其后的函数test定义之间,遗漏了;号,变成声明函数test,返回pic结构类型变量。如果;没有遗漏,test缺省返回int型。

switch/case中的break

    switch/case结构中,case后的模块中是否包含break,程序语法都正确,只是逻辑截然不同。如:

    switch(color)

    {

      case 1: printf ("red");

      case 2: printf ("yellow");

      case 3: printf ("blue");

    }

    假设color值是2,输出结果是yellowblue,而不是有人预想的yellow,因为一旦某case分支缺少break,流程执行完当前case后,自然转向下一case,直到break出现才跳出。这似乎让人费解,明明case 3并不成立,怎么它的条件分支也会执行?问题在于人们潜意识里总把case等同于if,但编译器并不这么认为。不要把switch/case简单和if/else画等号,C不会提供两个功能完全互换的功能。

    switch/case这种特殊流程是双刃剑。一方面,某些情况下故意去掉break,能方便实现if/else等其他语句很难实现的特殊控制流程。另一方面,写代码时不小心遗漏break语句,编译器不能识别并提醒,只会认为程序员有意这么做,包不包括break;都成立时,这种“程序员总正确”的推定就产生bug

形似的单双目运算符

    C语言为编译器以及编程的方便,最大程度“复用”一些字符,为使用概率高的操作分配较短的符号,以提高整体效率。&&&|||以及=“==”,都是这种情况,比如用=表示赋值而用==表示比较,因为赋值操作出现概率高于比较,所以为其分配更短的符号。

    尽管这种方法使C更加简洁,但由于符号极度相似,稍不注意,就会因笔误出现混淆。估计所有C程序员都犯过类似下面的错误:

    if(i=0)    //原意应为if(i == 0)

    这种下意识的随手错误很难避免,毕竟一个“=”号人们熟悉了几十年,而“==”号相对陌生。===方便了编译器,却给程序员们设了一条绊马索。甚至有些错误,除了作者本人调式,其他任何程序员也不可能检查出来,更别说编译器了。如:

if(a&b)  //原意if(a && b)

    &&&哪个都通,编译器不报错,其他人也不可能发现问题。但如果原来预想实现是a&&b,现在的实现就是bug。所以这几对单双目运算符使用时要多注意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值