运算优先级与结合性
困难9运算符优先级与结合性: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这样简洁的代码。
最后送大家一句话:记不住优先级?没关系,多敲几个括号,你的代码自己就是最好的文档! 括号不仅让计算机明白你的意图,更让读代码的人一眼看懂你想做什么。写代码不仅要让机器跑对,更要让人类看得舒服。加油!
例题精讲
给定声明:int a = 3, b = 4, c = 5; 表达式 a + b > c && c % 2 == 0 的值是?
执行以下代码后,变量 x 的值是 11,y 的值是 8。 int x = 3, y = 4; x += y *= 2;
以下程序输出结果为 13,请将表达式补充完整。
#include <iostream>
using namespace std;
int main() {
int a = 5, b = 9;
cout << ( ___ ) << endl;
return 0;
}已知 int a=2, b=3, c=4; 表达式 a>b ? a : b<c ? b : c 的结果是?
表达式 int x = (1, 2, 3); 执行后,变量 x 的值是 3。