问题的背景
C语言引入了运算符优先级是为了管理数学和逻辑表达式中的计算顺序。尽管数学运算符具有历史上预期的优先级(如代数中),但许多新运算符(逻辑、位运算、赋值等)的出现使情况变得复杂。为了降低错误的可能性并提高可读性,出现了官方的运算符优先级和结合性列表。
问题
由于运算符数量的增加及其不同的性质(算术、比较、赋值、逻辑、索引),在构造复杂表达式时会产生歧义。对它们使用顺序的错误理解会导致逻辑错误和不易察觉的bug,特别是在结合位运算符和逻辑运算符、指针、自增和三元运算符时。
解决方案
代码示例:
#include <stdio.h> int main() { int a = 1, b = 2, c = 3, d; d = a + b * c; // b*c 先执行:d = 1 + (2*3) = 7 printf("%d ", d); d = a + b << 1; // a + b = 3, 然后 3 << 1 (6) printf("%d ", d); d = a < b ? a++ : b++; printf("%d ", d); // a < b 为真 => d = a (1),a 在之后增加 }
关键特性:
表达式x = y > z ? y : z; 如果忘记括号,会得到什么结果?
答案:三元运算符?: 的优先级低于>。首先计算(y > z),然后在y和z之间选择。但是如果与赋值结合,可能会出现意想不到的效果。最好始终使用括号x = (y > z) ? y : z;。
表达式*p++的结果是什么,为什么?
答案:后增量运算符(++)的优先级高于解引用(),因此p++ 变为*(p++): 首先使用p并增加,只有之后才解引用,这可能与*++p(增加后的解引用)不同。
为什么表达式a & b == c并不能按预期工作?
答案:运算符==的优先级高于&,因此表达式被解析为a & (b == c),而不是(a & b) == c,这会产生意想不到的结果。为了获得所需的逻辑,需要使用括号。
if ((a & b) == c) { ... }
负面案例
在优化代码时,程序员在没有括号的情况下合并了几种运算符:if( mask & flag == 0 ) ...,结果检查逻辑工作不正确,导致系统崩溃。
优点:
缺点:
积极案例
使用明确的分组:if( (mask & flag) == 0 ) ...,逻辑清晰,容易改变标志。
优点:
缺点: