C陷阱与缺陷 第2章 语法“陷阱” 2.2 运算符的优先级问题

    运算符的优先级问题
    FLAG是一个整数,且该整数值的二进制表示中只有某一位是1,其余各位均是零,亦即该整数是2的某次幂。如果对于整数变量flags,我们需要判断它在常量FLAG为1的那一位是否同样也为1,通常可以这样写: 
    if (flags & FLAG) 
        ...
    if语句判断括号内表达式的值是否为0。
    考虑到可读性,如果对表达式的值是否为0的判断能够显式地加以说明,无疑使得代码自身就起到了注释该段代码意图的作用,其写法如下: 
    if (flags & FLAG != 0)
        ...
    这是一个错误的语句。因为!=运算符的优先级要高于&运算符,所以上式实际上被解释为: 
    if (flags & (FLAG != 0))
        ...
    因此,除了FLAG恰好为1的情形,FLAG为其他数时这个表达式都是错误的。 
    if ((flags & FLAG) != 0)
        ...
    hi和low是两个整数,它们的值介于0和15之间,如果r是一个8位整数,且r的低4位与low各位上的数一致,而r的高4位与hi各位上的数一致,很自然会想到要这样写: 
    r = hi << 4 + low;
    但是很不幸,这样写是错误的。加法运算的优先级要比移位运算的优先级高,因此本例实际上相当于: 
    r = hi << (4 + low);
    对于这种情况,有两种更正方法:第一种方法是加括号;第二种方法意识到问题出在程序员混淆了算术运算与逻辑运算,于是将原来的加号改为按位逻辑或,但这种方法牵涉到的移位运算与逻辑运算的相对优先级就更加不是那么明显。两种方法如下: 
    r = (hi << 4) + low;  //法1:加括号 
    r = hi << 4 | low;     //法2:将原来的加号改为按位逻辑或
    用添加括号的方法虽然可以完全避免这类问题,但是表达式中有了太多的括号反而不容易理解。因此,记住C语言中运算符的优先级是有益的。

                    完整的C语言运算符表 
    运算符                                                  结合性
    () [] -> .                                              自左向右
    ! ~ ++ -- - (type) * & sizeof                自右向左
    * / %                                                  自左向右
    + -                                                     自左向右
    << >>                                                自左向右 
    < <= > >=                                          自左向右 
    == !=                                                 自左向右 
    &                                                       自左向右 
    ^                                                        自左向右 
    |                                                         自左向右 
    &&                                                     自左向右 
    ||                                                        自左向右 
    ?:                                                       自右向左 
    assignments                                      自右向左 
    ,                                                         自左向右

    优先级最高者其实并不是真正意义上的运算符,包括数组下标、函数调用操作符以及各结构成员选择操作符。 它们都是自左向右结合。
    单目运算符的优先级仅次于前述运算符。在所有真正意义上的运算符中,它们的优先级最高。单目运算符是自右向左结合。
    优先级比单目运算符要低的,接下来就是双目运算符。在双目运算符中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,就是就是逻辑运算符、三目运算符、赋值运算符,最后是逗号运算符。
    译注:原书如此,条件运算符实际应为三目运算符。

    我们需要记住的最重要的两点是:
    1、任何一个逻辑运算符的优先级低于任何一个关系运算符;
    2、移位操作符的优先级比算术运算符低,但比关系运算符要高。

    6个关系运算符的优先级不同,运算符==和!=的优先级要低于其他关系运算符的优先级。 
 
    比较a与b的相对大小顺序是否和c与d的相对大小顺序一样,就可以这样写:                                       a < b == c < d
    任何两个逻辑运算符都具有不同的优先级。所有的按位运算符优先级要比顺序运算符的优先级高,每个“与”运算符要比相应的“或”运算符优先级高,而按位异或运算符(^运算符)的优先级介于按位与运算符和按位或运算符之间。

    这些运算符的优先顺序是由于历史原因形成的。B语言是C语言的“祖先”,B语言的逻辑运算符大致相当于C语言中的&和|运算符。虽然这些运算符从定义上而言是按位操作的,但是当它们出现在条件语句的上下文中时,B语言的编译器会把它们作为相当于现在C语言中的&&和||运算符来处理。而到了C语言中,这两种不同的用法被区分开来,从兼容性的角度来考虑,如果对它们优先顺序的改变过大,将是一件危险的事。
    三目运算符的优先级最低。这就允许我们在三目运算符的条件表达式中包括关系运算符的逻辑组合。 
    tax_rate = income > 4000 && residency < 5 ? 3.5 : 2.0;
    说明了赋值运算符的优先级低于条件运算符的优先级是有意义的。所有赋值运算符的优先级是一样的,而且它们的结合方式是自右向左,因此,
    home_score = visitor_score = 0;
    与下面两条语句所表达的意思是相同的:
    visitor_score = 0;
    home_score = visitor_score;
    在所有的运算符中,逗号运算符的优先级最低。这一点很容易记住,因为在需要一个表达式而不是一条语句中,经常使用逗号运算符来替换作为语句结束标志的分号。逗号运算符在宏定义中特别有用。 

    循环语句的本意是复制一个文件到另一个文件: 
    while (c = getc(in) != EOF)
        putc(c, out);
    在while语句的表达式中,c似乎是首先被赋予函数getc(in)的返回值,然后与EOF比较是否到达文件结尾以便决定是否终止循环。然而,由于复制运算符的优先级要低于任何一个比较运算符,因此c的值实际上是函数getc(in)的返回值与EOF比较的结果。此处函数getc(in)的返回值只是一个临时变量,在与EOF比较后就被“丢弃”了。因此,最后得到的文件“副本”中只包含一组二进制值为1的字节流。
    上例实际应该写成: 
    while ((c = getc(in)) != EOF)
        putc(c, out);

    if ((t = BTYPE(pt1->aty) == STRTY) || t == UNIONTY)
    这段代码的本意是首先赋值给t,然后判断t是否等于STRTY或者UNIONTY。实际的结果却大相径庭:根据BTYPE(pt1->aty)的值是否等于STRTY,t的取值或者为1或者为0;如果t为0,还将进一步与UNIONTY比较。
    正确的写入如下: 
    if ((t = BTYPE(pt1->aty)) == STRTY || t == UNIONTY)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值