运算符是基本的语言元素,虽然接触编程语言的年岁不短了,但有时面对着一些表达式,仍不能立马确认运算顺序。我相信很多人也有我这方面的疑惑,今天就把一些“设计错误”的运算符拉出来溜溜。

& 和 |

看下面的代码片段,变量a的值是多少呢?

    int a = 1 | 0 == 1 & 0;

对于表达式1 | 0 == 1 & 0,包含三个运算符,已知&优先级高于|,并且这些位操作符要么高于==,要么低于==,分两种情况讨论:

  1. 位操作符优先级高于==
    等价于(1 | 0) == (1 & 0),计算的结果为0
  2. ==优先级高于位操作符
    等价于1 | ((0 == 1) & 0)),计算的结果为1

将上面的代码运行打印会得到a的值为1。这说明==优先级高于位操作符。这或许是我最迷惑的运算符规则了,在我看来位操作符本质上是算术运算符,都是产生一个数,应该跟算术运算符的规则大体一致。故而,我认为第一种更合理。

是不是设计的错误呢,终于在《C专家编程》这本书里找到了答案,即下节。

'And' and 'AND' or 'Or' or 'OR'

来自C语言的创造者 Dennis Ritchie 的解释:

&&|| 操作符与 == 操作符的优先级关系问题是这样产生的。在 C 的早期,&&& 合用同一个操作符, ||| 也是如此(明白吗?)。 它继承了 B 和 BCPL 中的概念“真值上下文”。就是在 ifwhile 等后面需要一个布尔值的时候,&就被翻译成现在的 &&||。如果它们在一般的表达式里,就被解释成位操作符,也就是现在的样子。这个机制操作起来没有问题,但理解起来很困难(在真值上下文里,存在“顶层运算符”的概念)。

&| 的优先级跟现在一样。最初,在 Alan Snyder 的催促下,我在C语言中加入了 &&||操作符。这就成功地把位运算的概念和布尔运算的概念分了开来。然而,我心怀不安,因为我意识到了优先级的问题。例如,在现存的大量程序中,存在诸如这样的表达式: if(a == b & c == d)

事后回想,如果我们一开始就改变优先级,让&的优先级高于==,在逻辑上可能更清晰一些。但是,从安全的角度出度,只能做到把 &&& 分开这个程度,无法在两者的优先级之间再插入其他的操作符(否則的话,现有的大量代码都有可能出问題)。

从上面的描述可以看出,最终设计的结果在于代码兼容性上的考量。JavaC# 的操作符优先级跟C/C++ 大同小异,这可以认为是另一个层次的兼容:程序员的编码习惯。

C++其他不合理的操作符

在《C专家编程》这本书里还列出了其他错误的操作符,这里一并列出:

优先级问题 表达式 人们可能误以为的结果 实际结果
.的优先级高于*。->优先级用于消除这个问题 *p.f p所指对象的字段f。 (*p).f 对p取f偏移,作为指针,然后进行间接引用操作。*(p.f)
[]高于* int *ap[] ap是个指向int数组的指针。int(*ap)[] ap是个元素为int指针的数组。int *(ap[])
函数()高于* int *fp() fp是个函数指针,所指函数返回int。int (*fp)() fp是个函数,返回int。int(fp())
==和!=高于位操作符 (val & mask != 0) (val & mask)!= 0 val & (mask != 0)
==和!=高于赋值符 c=getchar()!=EOF (c=getchar())!=EOF c=(getchar()!=EOF)
算术运算符高于移位运算符 msb << 4 + lsb (msb << 4) + lsb msb << (4 + lsb)
逗号运算符在所有运算符中优先级最低 i=1,2 i=(1,2) (i=1),2

这些不合理的操作符毕竟占了少数,遇到它们完全可以使用括号保证可靠性,同时不会影响代码的可读性。

订阅最新的文章请关注我的公众号:

图片描述


ideami
91 声望11 粉丝