C++左结合,右结合的概念,与运算符的优先级有什么关系

什么是结合性?

结合性决定了当同一个表达式中出现多个相同优先级的运算符时,运算的执行顺序。

它主要解决两种问题:

  • 左结合:从左向右计算。
  • 右结合:从右向左计算。

左结合

大多数运算符是左结合的。这意味着运算从表达式的最左边开始。

例子:a - b - c

  • 运算符 - 是左结合的。
  • 它被解释为 (a - b) - c
  • 先计算 a - b,然后用其结果减去 c

其他左结合运算符的例子:

  • a + b + c(a + b) + c
  • a * b * c(a * b) * c
  • a / b / c(a / b) / c
  • a && b && c(a && b) && c

右结合

少数运算符是右结合的。这意味着运算从表达式的最右边开始。

例子1:a = b = c

  • 运算符 = 是右结合的。
  • 它被解释为 a = (b = c)
  • 先执行 b = c,然后将这个赋值表达式的结果(即 c 的值)再赋给 a

例子2:*p++

  • 这里 *++(后缀)优先级相同,但它们是右结合的。
  • 它被解释为 *(p++),而不是 (*p)++。先执行 p++,然后对自增前的地址解引用。

其他右结合运算符的例子:

  • !~a!(~a) (一元运算符)
  • a ? b : c ? d : ea ? b : (c ? d : e) (条件运算符)

结合性与优先级的关系

这是一个关键区别:

特性 优先级 结合性
解决的问题 不同运算符之间,谁先执行? 相同优先级的运算符之间,谁先执行?
类比 乘法和加法,先算乘法 多个连加的加法,从左往右算
例子 a + b * ca + (b * c) a + b + c(a + b) + c

总结关系:

  1. 优先级第一:首先根据优先级确定哪些操作先绑定。
  2. 结合性第二:当优先级无法决定顺序时(即运算符相同时),再用结合性来决定。

经典示例分析

x = a + b * c > d && e || f

  1. 优先级最高*b * c
  2. 优先级次高+a + (b * c)
  3. 优先级第三>(a + b * c) > d
  4. 优先级第四&&((a + b * c) > d) && e
  5. 优先级第五||(左结合) → [((a + b * c) > d) && e] || f
  6. 优先级最低=(右结合) → x = ([((a + b * c) > d) && e] || f)

最终等价于:x = ( ( (a + (b * c)) > d ) && e ) || f )


核心要点

  • 优先级解决“先乘除后加减”的问题。
  • 结合性解决“多个相同运算符如何分组”的问题。
  • 当不确定时,使用括号是确保表达式按你意图执行的最佳实践。

运算符优先级表

优先级 运算符 描述 结合性
1 :: 作用域解析 从左到右
2 a++ a-- 后缀自增/自减 从左到右
type() type{} 函数风格转换 从左到右
a() 函数调用 从左到右
a[] 下标 从左到右
. -> 成员访问 从左到右
3 ++a --a 前缀自增/自减 从右到左
+a -a 一元正负 从右到左
! ~ 逻辑非、按位非 从右到左
(type) C风格转换 从右到左
*a 解引用 从右到左
&a 取地址 从右到左
sizeof 大小查询 从右到左
new new[] delete delete[] 动态内存 从右到左
4 .* ->* 成员指针访问 从左到右
5 * / % 乘、除、取模 从左到右
6 + - 加、减 从左到右
7 << >> 位左移、右移 从左到右
8 <=> 三路比较(C++20) 从左到右
9 < <= > >= 关系比较 从左到右
10 == != 相等比较 从左到右
11 & 按位与 从左到右
12 ^ 按位异或 从左到右
13 | 按位或 从左到右
14 && 逻辑与 从左到右
15 || 逻辑或 从左到右
16 ?: 条件运算符 从右到左
= 赋值 从右到左
+= -= *= /= %= 复合赋值 从右到左
<<= >>= 位运算复合赋值 从右到左
&= ^= |= 位运算复合赋值 从右到左
17 , 逗号运算符 从左到右

重要说明

  1. 结合性

    • 大部分从左到右
    • 一元、赋值、条件运算符从右到左
  2. 记忆技巧

    • 算术 > 移位 > 比较 > 位运算 > 逻辑 > 条件 > 赋值 > 逗号
    • 一元运算符优先级很高
    • 后缀高于前缀
  3. 最佳实践

    • 不确定时使用括号明确优先级
    • 避免编写过于复杂的表达式