CC++ & Algorithm

运算优先级与结合性

困难9
语言版本:C++
概述:搞清多个运算符同时出现时谁先算,避免“算错”的尴尬。

运算符优先级与结合性:C++里的“先算谁”规则

你是不是也遇到过这种情况:写了一个挺简单的表达式,结果算出来的数字却和自己心里想的不一样?别着急,大概率不是计算器坏了,而是你忘了C++里一堆运算符同时出现时,谁先算谁后算的“规矩”。这就像数学课上学过的“先乘除后加减”,在C++里也有类似的规则,叫做 运算符优先级。优先级高的先算,优先级相同的按 结合性 决定是从左到右算,还是从右到左算。

下面我们就把这些规则掰开揉碎了讲一讲,再配上生活里的小例子,帮你彻底搞懂。


一、什么是运算符优先级?

简单说,优先级就是“谁更厉害谁先算”。就像打游戏时,大招的伤害数值高,所以先放出来。C++里运算符也分等级,等级高的(优先级大的)先执行。

举个例子,数学里 3 + 4 × 5,乘法优先级高,所以先算 4 × 5 = 20,再加 3 得到 23。C++里完全一样:

int a = 3, b = 4, c = 5;   // 定义三个整数
int result = a + b * c;     // 先算 b * c = 20,再算 a + 20 = 23

如果反过来想让加法先算,就要用小括号 () 强行提高优先级,就像在学校里老师点名说“这两个先算”:

int result2 = (a + b) * c;   // 先算 a + b = 7,再算 7 * 5 = 35

生活中的类比:排队买零食,老师让“高年级同学先买”。优先级就是年级高低,高的先买。但如果你喊一声“老师,这两个同学我要他们一起先买!”(加括号),那就按你说的顺序来。


二、常见运算符的优先级表(从高到低)

C++里运算符可多了,不用全背下来,但常用的几个得心里有数。下面这张表把优先级从最高(1)到最低(13)排了个序,越靠上的越先算。

优先级运算符分类具体运算符举例结合性
1后缀自增自减x++, x--从左到右
2前缀自增自减、一元负号、逻辑非等++x, --x, -x, !x, &x, *x从右到左
3乘、除、取余*, /, %从左到右
4加、减+, -从左到右
5移位(按位移动)<<, >>从左到右
6关系运算(小于、大于等)<, >, <=, >=从左到右
7相等不等于==, !=从左到右
8按位与(位运算)&从左到右
9按位异或^从左到右
10按位或``
11逻辑与(并且)&&从左到右
12逻辑或(或者)`
13赋值、复合赋值=, +=, -=, *=, /=从右到左

注意:赋值运算符(=)以及像 +=-= 这样的复合赋值,它们是从右向左结合的。什么意思?看下面这句:

int a, b, c;         // 定义三个变量
a = b = c = 5;       // 赋值从右向左:先 c = 5,再 b = c(b变成5),最后 a = b(a变成5)

是不是很像传话游戏?最后一句“传给前一个人”,从右边开始一个个往左传。


三、结合性:当优先级一样时怎么办?

优先级相同的时候,结合性就派上用场了。绝大多数运算符都是从左到右结合,意思是算完左边再算右边。比如加法减法从左到右:

int x = 10 - 3 - 2;   // 先算 10 - 3 = 7,再算 7 - 2 = 5

但赋值运算符是个“反着走”的家伙,它是从右到左结合。我们刚才看到了 a = b = c = 5 的例子。

还有一个常见的从右到左的是前缀自增自减

int y = 5;
int z = ++y;   // 先给 y 加 1 变成 6,再把 6 赋给 z

如果写了 ++y 两次?比如 ++++y?编译器会报错,因为 ++ 不是用来连写的,我们理解成单独的运算符就好。


四、新手最容易踩的三个坑

坑1:误以为 &&|| 优先级高,但忘了括号

逻辑与 && 确实比逻辑或 || 优先级高,所以下面两条语句结果可能不同:

bool a = false, b = true, c = false;  // 定义三个布尔值
bool r1 = a || b && c;                // 先算 b && c = false,再算 a || false = false
bool r2 = (a || b) && c;              // 先算 a || b = true,再算 true && c = false

如果想表达“A或B,并且同时满足C”,一定要加括号 (a || b) && c,否则就变成“A 或者(B且C)”了。

坑2:自增自减用混了,以为 x++++x 没区别

看到表里 x++(后缀)优先级是第1级,++x(前缀)是第2级,实际上它们在表达式里的运算时机不一样。比如:

int num = 10;
int result1 = num++ * 2;   // 先取 num 的值(10)参与乘法,得 20,然后 num 变成 11
int result2 = ++num * 2;   // 先给 num 加 1(变成 12),再用 12 乘 2,得 24

小口诀:后缀“用完再加”,前缀“加完再用”。

坑3:把赋值 = 和相等 == 搞混

赋值优先级非常低(第13级),而相等运算 == 是第7级。所以下面这行代码:

int x = 5, y = 6;            // 定义两个整数
bool flag = x = y == 7;      // 你想比较 x 和 y 是否相等?不对!
// 实际运算顺序:先算 y == 7,得 false(0),然后赋值 x = 0,再把 0 赋给 flag

这通常不是你想要的效果。正确写法应该用括号明确:bool flag = (x == y);


五、完整示例:生活中的算账

假设小明和小红一起买零食,小明有零花钱 30 元,小红有 20 元,他们分别买了单价 3 元的糖果,小明买了 5 颗,小红买了 3 颗。最后看看两人还剩多少钱,以及谁花的钱多。

#include <iostream>
using namespace std;

int main() {
    int ming_money = 30, hong_money = 20;  // 小明和小红的初始零花钱
    int candy_price = 3;                   // 每颗糖果3元
    int ming_buy = 5, hong_buy = 3;        // 分别买了几颗

    // 分别计算总花费:花费 = 单价 * 颗数
    int ming_cost = candy_price * ming_buy;  // 3 * 5 = 15
    int hong_cost = candy_price * hong_buy;  // 3 * 3 = 9

    // 计算剩余零花钱
    int ming_left = ming_money - ming_cost;  // 30 - 15 = 15
    int hong_left = hong_money - hong_cost;  // 20 - 9 = 11

    // 判断谁花的钱多(使用关系运算符和逻辑运算符)
    // 注意:&& 优先级高于 ||,所以这句相当于: (ming_cost > hong_cost) || (ming_cost == hong_cost && ming_buy > hong_buy)
    bool ming_spend_more = ming_cost > hong_cost || ming_cost == hong_cost && ming_buy > hong_buy;

    cout << "小明剩余: " << ming_left << "元" << endl;
    cout << "小红剩余: " << hong_left << "元" << endl;
    cout << "小明花得更多吗? " << (ming_spend_more ? "是的" : "不是") << endl;

    // 下面再展示一下赋值从右向左
    int a, b, c;            // 定义三个整数
    a = b = c = 100;        // c先变成100,b变成100,a变成100
    cout << "a, b, c的值均为: " << a << ", " << b << ", " << c << endl;

    return 0;
}

运行结果:

小明剩余: 15元
小红剩余: 11元
小明花得更多吗? 是的
a, b, c的值均为: 100, 100, 100

注意:那个判断 ming_spend_more 的表达式里,&&|| 优先级高,所以先算 ming_cost == hong_cost && ming_buy > hong_buy,但这里 ming_cost 是15,hong_cost 是9,不相等,所以整个 && 部分是 false。然后算 ||,左边 ming_cost > hong_cost 是 true,所以结果为 true。逻辑是正确的。


六、相关知识点指引

学会了优先级和结合性,你就能读懂大部分表达式了。如果想进一步探索,可以看看这些知识:

  • 算术运算符+ - * / %,它们优先级中上,记得乘除取余高于加减。
  • 关系运算符与逻辑运算符:常用于条件判断,比如 if 语句、循环条件里,特别要注意优先级顺序:关系 > 相等 > 逻辑与 > 逻辑或。
  • 自增自减运算符:前缀和后缀的区别,以及它们在不同表达式里的细微差异。
  • 位运算& | ^ << >> 在竞赛中很实用,优先级相对较低,建议多用括号。
  • 赋值与复合赋值:优先级最低,而且从右向左结合,这个特性可以写出像 a = b = c = 0 这样简洁的代码。

最后送大家一句话:记不住优先级?没关系,多敲几个括号,你的代码自己就是最好的文档! 括号不仅让计算机明白你的意图,更让读代码的人一眼看懂你想做什么。写代码不仅要让机器跑对,更要让人类看得舒服。加油!

例题精讲

1单选题

给定声明:int a = 3, b = 4, c = 5; 表达式 a + b > c && c % 2 == 0 的值是?

Atrue
Bfalse
C1
D0
2判断题

执行以下代码后,变量 x 的值是 11,y 的值是 8。 int x = 3, y = 4; x += y *= 2;

3填空题
以下程序输出结果为 13,请将表达式补充完整。
#include <iostream>
using namespace std;
int main() {
    int a = 5, b = 9;
    cout << ( ___ ) << endl;
    return 0;
}
4单选题

已知 int a=2, b=3, c=4; 表达式 a>b ? a : b<c ? b : c 的结果是?

A2
B3
C4
D不确定
5判断题

表达式 int x = (1, 2, 3); 执行后,变量 x 的值是 3。