Skip to content

运算符优先级

鉴于算术运算符比较简单,所以我们放在最开始讲。然后下面我们要讲C语言运算符的两个非常重要的性质:

    • 左结合性:意味着具有相同优先级的运算符将从左到右进行计算。

    • 右结合性:意味着具有相同优先级的运算符将从右到左进行计算。

搞清楚它们,对于分析清楚一个复杂的C语言的表达式至关重要。

下面这张表格就罗列出了常见的运算符的优先级,以及它的结合性,其中:

中文版

版本一

版本2

由于 * 的优先级高于 + ,因此 i + j * k 等价于 i + (j * k)

当表达式中包含两个或者更多个具有相同优先级的运算符时,仅有运算符优先级规则是不够的。在这种情况下,运算符的结合性开始发挥作用。如果运算符是从左向右结合的,那么这种运算符是左结合的。二元运算符大多是左结合的:

cpp
i - j + k 等价于 (i - j) + k
i * j / k 等价于 (i * j) / k

如果运算符是从右向左结合的,那么称这种运算符是右结合的。一元运算符大多是右结合的:

cpp
-+i 等价于 -(+i)

关于运算符优先级和结合性的建议

运算符的优先级和结合性显然是运算符非常核心的重要概念,但强行把这张表格记下来显然是不现实的,所以我们给出以下总结和建议:

  1. 一元运算符的优先级总是高于二元运算符。
  2. 赋值运算符(包括复合赋值运算符)的优先级几乎是最低的。这很好理解,如果运算还没结束,赋值就完成了,这不是想看到的。
  3. 在实践中不要写出非常长,非常复杂、可读性非常差的的表达式。优秀的程序员不应以"写出别人不理解的代码"为荣:
    • 如果表达式有过长的趋势,不妨改成两个式子
    • 表达式中即便优先级没问题,也最好用合适的小括号括起来关键位置,明确代码意图,增强代码可读性。
  4. C语言经历了漫长的发展,有很多关于复杂表达式的惯用法,除此接触它们你可能会很头疼,但基于习惯,我们还是建议程序员记住它们。

以上。

举例说明

现在根据这张表格,我们尝试来分析几个复杂表达式的运算过程:

p是一个指向int变量的指针类型,对于表达式int num = *p++,该表达式是如何进行计算的呢?分析如下:

  1. 后缀自增自减符号在表达式中拥有最高的计算优先级,所以整个表达式中p++最先进行计算。
  2. 但后缀形式的p++,它的主要作用是直接返回当前p的值,副作用是将p的值加1。
  3. 所以*(p++)就是*p,也就是对指针p执行解引用运算。
  4. 最后执行赋值运算符=,将*p的结果赋值给变量num。整个表达式执行完毕。
  5. 当然,整个表达式执行完毕后,p会自增1。这是表达式p++带来的副作用。
  6. 整个表达式的运算过程是:将*p的结果赋值给变量num,并且p指针自增1。

对于表达式int num = ++*p,该表达式是如何进行计算的呢?分析如下:

  1. 在表达式 ++*p 中,前缀自增运算符 ++ 和解引用运算符 * 都作用于指针 p
  2. 虽然,前缀自增运算符优先级要比解引用运算符高,但它必须结合一个操作数才能计算,根据书写的位置,前缀运算符只能结合*p。所以整个表达式最先计算的是*p
  3. 前缀形式意味着主要作用是返回自增自减后的结果,副作用是自增自减1。
  4. 所以=会将*p的结果自增1后再赋值给变量num。
  5. 整个表达式的运算过程是:将*p的结果自增1后赋值给变量num。

首先看一个简单的例子:

a + b - c或者a * b / c如何计算呢?

任何人都会觉得很简单——从左往右运算呗,那么为什么呢?

这就是结合性的作用:两个表达式中的运算符分别都具有相同的优先级,那么考虑运算顺序就需要考虑它们的结合性。而它们的结合性是左结合的,于是整个表达式的运算顺序就是从左往右计算。

再举一个例子:

假如s是一个Student结构体对象,math成员表示Student对象的数学成绩。那么表达式s.math++是如何运算的呢?

再假如p是一个指向s对象的指针,那么表达式p->math++是如何运算的呢?

也许你还不懂结构体的语法,但没有关系。->++.这三个运算符显然具有同样的优先级,于是它们之间的运算就需要考虑结合性。

而这些运算符的结合性都是左结合性,于是整体的运算就是从左往右的——先获取s对象的math数学成绩,然后将这个成绩自增1。

对于数组的一个区间[a, b],表达式(b - a >> 1) + a的含义是什么?

表达式 (b - a >> 1) + a 是用来计算区间 [a, b] 的中间值的。

  1. b - a 计算出区间的长度。(算术运算符在所有二元运算符中优先级最高,先计算)
  2. >> 1 是右移一位操作,相当于除以 2,用于将区间长度除以 2,得到中间值的偏移量。
  3. 最后再加上 a,得到的就是区间 [a, b] 的中间值。

相信结合这些例子,你对运算符的优先级和结合性都有一定的理解了。