首页 百科知识 运算符并不总是具有你所想象的优先级

运算符并不总是具有你所想象的优先级

时间:2022-09-22 百科知识 版权反馈
【摘要】:它们具有真正的运算符中的最高优先级。还有就是六个关系运算符并不具有相同的优先级:==和!这就允许我们判断a和b是否具有与c和d相同的顺序,例如: 在逻辑运算符中,没有任何两个具有相同的优先级。另外,所有的复合赋值运算符具有相同的优先级并且是自右至左结合的,因此 具有最低优先级的是逗号运算符。C中的逻辑运算符的优先级具有历史原因。

 

2.2 运算符并不总是具有你所想象的优先级

    假设有一个声明了的常量FLAG,它是一个整数,其二进制表示中的某一位被置位(换句话说,它是2的某次幂),并且你希望测试一个整型变量flags该位是否被置位。通常的写法是:

 

if(flags & FLAG) ...

 

其意义对于很多C程序员都是很明确的:if语句测试括号中的表达式求值的结果是否为0。出于清晰的目的我们可以将它写得更明确:

 

if(flags & FLAG != 0) ...

 

这个语句现在更容易理解了。但它仍然是错的,因为!=&绑定得更紧密,因此它被分析为:

 

if(flags & (FLAG != 0)) ...

 

这(偶尔)是可以的,如FLAG10(!)的时候,但对于其他2的幂是不行的[2]

 

    假设你有两个整型变量,hl,它们的值在015(含015)之间,并且你希望将r设置为8位值,其低位为l,高位为h。一种自然的写法是:

 

r = h << 4 + 1;

 

不幸的是,这是错误的。加法比移位绑定得更紧密,因此这个例子等价于:

 

r = h << (4 + l);

 

正确的方法有两种:

 

r = (h << 4) + l;

 

r = h << 4 | l;

 

    避免这种问题的一个方法是将所有的东西都用括号括起来,但表达式中的括号过度就会难以理解,因此最好还是是记住C中的优先级。

 

    不幸的是,这有15个,太困难了。然而,通过将它们分组可以变得容易。

 

    绑定得最紧密的运算符并不是真正的运算符:下标、函数调用和结构选择。这些都与左边相关联。

 

    接下来是一元运算符。它们具有真正的运算符中的最高优先级。由于函数调用比一元运算符绑定得更紧密,你必须写(*p)()来调用p指向的函数;*p()表示p是一个返回一个指针的函数。转换是一元运算符,并且和其他一元运算符具有相同的优先级。一元运算符是右结合的,因此*p++表示*(p++),而不是(*p)++

 

    在接下来是真正的二元运算符。其中数学运算符具有最高的优先级,然后是移位运算符、关系运算符、逻辑运算符、赋值运算符,最后是条件运算符。需要记住的两个重要的东西是:

 

所有的逻辑运算符具有比所有关系运算符都低的优先级。

移位运算符比关系运算符绑定得更紧密,但又不如数学运算符。

    在这些运算符类别中,有一些奇怪的地方。乘法、除法和求余具有相同的优先级,加法和减法具有相同的优先级,以及移位运算符具有相同的优先级。

 

    还有就是六个关系运算符并不具有相同的优先级:==!=的优先级比其他关系运算符要低。这就允许我们判断ab是否具有与cd相同的顺序,例如:

 

a < b == c < d

 

    在逻辑运算符中,没有任何两个具有相同的优先级。按位运算符比所有顺序运算符绑定得都紧密,每种与运算符都比相应的或运算符绑定得更紧密,并且按位异或(^)运算符介于按位与和按位或之间。

 

    三元运算符的优先级比我们提到过的所有运算符的优先级都低。这可以保证选择表达式中包含的关系运算符的逻辑组合特性,如:

 

z = a < b && b < c ? d : e

 

    这个例子还说明了赋值运算符具有比条件运算符更低的优先级是有意义的。另外,所有的复合赋值运算符具有相同的优先级并且是自右至左结合的,因此

 

a = b = c

 

 

b = c; a = b;

 

是等价的。

 

    具有最低优先级的是逗号运算符。这很容易理解,因为逗号通常在需要表达式而不是语句的时候用来替代分号。

 

    赋值是另一种运算符,通常具有混合的优先级。例如,考虑下面这个用于复制文件的循环:

 

while(c = getc(in) != EOF)

    putc(c, out);

 

这个while循环中的表达式看起来像是c被赋以getc(in)的值,接下来判断是否等于EOF以结束循环。不幸的是,赋值的优先级比任何比较操作都低,因此c的值将会是getc(in)EOF比较的结果,并且会被抛弃。因此,“复制”得到的文件将是一个由值为1的字节流组成的文件。

 

    上面这个例子正确的写法并不难:

 

while((c = getc(in)) != EOF)

    putc(c, out);

 

然而,这种错误在很多复杂的表达式中却很难被发现。例如,随UNIX系统一同发布的lint程序通常带有下面的错误行:

 

if (((t = BTYPE(pt1->aty) == STRTY) || t == UNIONTY) {

 

这条语句希望给t赋一个值,然后看t是否与STRTYUNIONTY相等。而实际的效果却大不相同[3]

 

    C中的逻辑运算符的优先级具有历史原因。B语言——C的前辈——具有和C中的&|运算符对应的逻辑运算符。尽管它们的定义是按位的,但编译器在条件判断上下文中将它们视为和&&||一样。当在C中将它们分开后,优先级的改变是很危险的[4]

 

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈