运算符优先级对照

选一门语言查它的运算符优先级表,或者搜一个运算符,看它在各语言里分别排在第几层、怎么结合。

层级序号只在同一门语言内部可比,所以展示时写的是「第几层 / 共几层」。搜中文名称也能命中,比如输入「幂」会找到所有语言里的幂运算。

使用说明

运算符优先级描述的是一串没有括号的表达式该按什么顺序归约。同一门语言里每个运算符都落在某一层,层号越小越先算:乘除在加减之上,加减在比较之上,比较在逻辑运算之上,赋值和逗号压在最底下。这张表把每门语言最高的一层编成 1 往下排,但层级编号只在语言内部有效——JavaScript 分了 20 层、Go 连一元层算上也只有 6 层,层数差异反映的是分档粗细,不能拿一个语言的「第 5 层」和另一个语言的「第 5 层」直接比。这也是页面统一显示「第几层 / 共几层」的原因。

结合性管的是同一层里的多个运算符谁先算。左结合的 a - b - c 等于 (a - b) - c,右结合的 a = b = c 等于 a = (b = c),幂在多数语言里也是右结合。把优先级和结合性混在一起看是最常见的误解:优先级解决不同层的先后,结合性解决同层的先后,两者都写在表里,缺一不可。至于求值顺序,那是第三件事——括号能改变结合方式,却改变不了函数参数的求值顺序,涉及副作用的表达式要靠拆语句来保证顺序。

下面这几个例子是真实项目里最容易出事的地方,共同点是「看起来像人话,实际绑定的位置和直觉相反」。

a & b == c

C、C++、Java、JavaScript、C#:相等比较比按位与高,表达式实际是 a & (b == c)。因为 == 只给出 0 或 1,结果几乎永远是 0。想比较掩码必须写 (a & b) == c。Go 是唯一相反的语言:它把 & 放进比比较更高的乘法组,同一个式子天然读作 (a & b) == c,不用额外加括号。

1 << 2 + 3

C、C++、Java、JavaScript、PHP、Rust:加法比位移高,所以先算 2 + 3 得 5,再左移 5 位,结果是 32 而不是 8。要让位移先算必须写成 (1 << 2) + 3。C# 也遵循同样的相对顺序,而 Go 和 Python 把位移放进了比加法更高的一层,同一个式子在那两门语言里是 (1 << 2) + 3 等于 7。

not a == b

Python:not 的优先级低于比较运算符,所以它等价于 not (a == b),不是 (not a) == b。这正是「英文运算符读起来像自然语言,但绑定顺序并不按直觉」的典型。

2 ** 3 ** 2

Python、JavaScript、Ruby、PHP:幂运算右结合,等于 2 ** (3 ** 2) = 512,不是 (2 ** 3) ** 2 = 64。同一个符号在 C 家族里根本不存在,在 SQL 里 PostgreSQL 又改成左结合,跨语言搬运公式时要格外小心。

数据的取舍也要交代清楚。这一页只收录各语言官方文档里明确列出的条目,拿不准的宁可不写;SQL 那一栏按 PostgreSQL 的官方文档整理,因为运算符优先级在 SQL 里由各引擎自行定义,MySQL、Oracle、SQL Server 在字符串拼接和整除这几处的行为并不一致,跨库查询请以目标引擎的手册为准。想验证某个表达式到底算出什么,可以把式子丢进科学计算器;整理 SQL 语句用SQL 格式化;确认压缩后的代码有没有因为括号被优化掉优先级,用JS 压缩美化。

常见问题

优先级和结合性有什么区别?

优先级决定「哪些运算先算」,只在不同的运算符之间起作用;结合性解决的是「同一个运算符连着写时从哪头算」,只在优先级相同的运算符之间起作用。a + b * c 由优先级决定先算乘法,而 a - b - c 的两个减号优先级相同,靠左结合决定等价于 (a - b) - c。两栏都要看,只看优先级会漏掉一半。

不同语言的层级数字能直接比吗?

不能。层级序号只在同一门语言内部有意义:这张表把每门语言最高的一层编成 1,但 JavaScript 有 20 层、Go 连一元层算上也只有 6 层,层数多少反映的是分档的粗细——Go 干脆不给运算符重载,所以表很短——不是严格程度。所以页面显示的是「第几层 / 总共几层」。要比的是「某个运算符相对于这门语言里其他运算符的位置」,比如它在加法之上还是之下。

拿不准的时候直接加括号不就行了?

加括号确实是最省事的做法,但它只解决可读性,不改变语义,而且优先级和求值顺序是两回事。a() + b() 里两个函数的调用顺序在 C 和 C++ 里是不确定的,加括号也管不了;f(a, b) 里参数的求值顺序同样没有规定。所以括号能帮你避开优先级坑,但涉及副作用的表达式还得靠拆成多条语句来保证顺序。

a & b == c 为什么会算错?

因为所有从 C 家族继承下来的语言里,相等比较的优先级都高于按位与。表达式实际读作 a & (b == c),而比较的结果只有 0 或 1,于是整个式子几乎永远是 0。想判断掩码必须写 (a & b) == c。Go 是这里唯一相反的语言:它把 & 归进比比较更高的乘法组,同一个式子天然读作 (a & b) == c。

1 << 2 + 3 得到的是 8 还是 32?

是 32。加法的优先级高于位移,所以先算 2 + 3 得 5,再左移 5 位。要让位移先算必须写成 (1 << 2) + 3。这条相对顺序源自 C 的传统,C、C++、Java、JavaScript、PHP、Rust、C# 都沿用;只有 Go 和 Python 把位移放到了比加法更高的一层,同一个式子在那两门语言里等于 7。

Python 的 not a == b 到底是什么意思?

等价于 not (a == b),也就是「a 不等于 b」,而不是 (not a) == b。Python 把 not 放在比较运算符之下,所以英文读起来像自然语言的写法,绑定顺序也符合自然语言。真正反直觉的恰恰是很多人凭直觉以为 not 会先作用于 a。

2 ** 3 ** 2 为什么是 512?

因为幂运算右结合,等于 2 ** (3 ** 2) = 2 ** 9 = 512,而不是 (2 ** 3) ** 2 = 64。Python、JavaScript、Ruby、PHP 的幂都是右结合;PostgreSQL 的 ^ 反而是左结合,同一个数学表达式搬过去结果就变了。幂和一元负号的搭配更麻烦:Python 里幂高于一元负号,所以 -2 ** 2 是 -(2 ** 2) 等于 -4;JavaScript 干脆把 -2 ** 2 定为语法错误,必须写成 -(2 ** 2) 才通过解析。

SQL 那一栏为什么标着 PostgreSQL?

因为运算符优先级在 SQL 里不属于标准的一部分,各引擎的实现差异不小:字符串拼接、整除取余、日期加减这几处尤其不一致,同一句查询在 PostgreSQL 能跑、换个引擎可能就换了语义。这一页只保证 PostgreSQL 那一栏的准确性,来源是它的官方文档。写跨库的 SQL 时,涉及运算符混用一律加括号,并且以目标引擎的手册为准。

相关:SQL 格式化科学计算器JS 压缩美化