一文搞懂C++常见运算符优先级
一文搞懂C++常见运算符优先级
对于初学 C++ 的同学来说,第一个让人混乱的便是 C++ 中复杂的算术表达式。下面是一个简单的例子:
- 例题1:
int a = 1; int b = 2; int c = 3; int d = 4; int e = 5; int result = a + b * c / d - e; //result = -3
- 例题2:
int a = 5; int b = 2; int c = ++a + b++ * 2; //c = 10 int d = a++ + (++b) * 2; //d = 14
看完结果之后你可能不相信我写的答案,没关系!实践出真知,下面为运行结果
看看你的猜测是否正确?如果你能正确并完整地说出代码的运算顺序和结果,那么恭喜你,至少掌握了基本的运算规律😎
结论
嘿,看到这里了,你可能会好奇,我们应该如何理解表达式的运算顺序呢🤔?
先说结论,在我们常见的算术表达式运算中:
后置递增/减 优先运算,其次是 前置递增/减,最后是四则运算(加减乘除)。其中,四则运算的顺序与我们小学数学的顺序一样,遵循先乘除后加减
其次,若表达式中有括号的,先计算括号内的表达式
前置&后置递增和递减
觉得很难理解?没关系,下面我们使用代码来验证和加深理解
//首先我们先看递增/递减,这里我们统一以递增为例
//1.前置递增
int a = 10;
++a; //让变量+1
cout << "a = "<< a << endl; //11
//2.后置递增
int b = 10;
b++; //让变量+1
cout << "b = " << b << endl; //11
看到这里,你不经会问,这不都是一样的吗?有什么区别?你的问题非常好,思考的很全面。单独使用的时候确实没什么区别,但是放在表达式中可就不一样了
//你能说出b和c的值吗?
//假如把 a = 5 这行代码去掉,那么b和c的值会变化吗?
int a = 5;
int b = ++a + 2;
a = 5;
int c = a++ + 2;
我们先不看答案,先来看递增的规律:
- 前置递增:先让变量+1 然后进行表达式的运算
- 后置递增:先进行表达式运算,后让变量+1
那么读到这里,你是不是以及开始尝试求解了?我们一起来看看思路和运算顺序
int a = 5;
//首先看这个式子,因为++符号在变量名前面,所以是前置递增
int b = ++a + 2;
//根据前置递增的运算规则,先让变量+1 然后进行表达式的运算。给出如下运行步骤
1. a先自增1,那么现在a = 6
2. 自增后的a = 6再与2相加
3. 故 b = 8
看到这里,你会觉得 前置递增++a
与 a = a + 1
的逻辑很相似。确实,你可以这么理解,但是这种理解仅限于 前置递增/减!至于为什么,我们再来看下面的解析
int a = 5;
int b = ++a + 2;
//把a的值重新置为5
a = 5;
//因为++符号在变量名前面,所以是后置递增
int c = a++ + 2;
//根据后置递增的运算规则,先进行表达式运算,后让变量+1。给出如下运行步骤
1. 因为是先进行表达式运算,那么先执行 a + 2
2. 那么此时 c = a + 2,故 c = 7
3. 最后a自增1,那么现在a = 6
假如你这时候还认为既然 ++a
相当于a = a+1
,那么 a++
也相当于 a = a+1
的思路来看待这个问题就大错特错了🦆,为什么?我们接下来再看看
//你在编译器里打出这几行行代码对比一下
int a = 5;
cout << a++ << endl; //a = 5
a = 5;
cout << (a = a + 1) << endl; //a = 6
你可以看到,结果是不是完全和我们想象的不一样?a++
居然输出的结果是5不是6?而 a = a+1
却输出的是6,这段代码就充分说明了为什么 a++
相当于 a = a+1
这个思路的不正确性
这时细心的同学可能会发现,为什么每次我做完操作之后,多一行 a = 5
?
这是个非常棒的问题,说明你注意到了这行代码的特殊性。还是如此,我们解释原理步骤,再运行代码
假设我把 a = 5
这行代码注释了,我们再来看b和c的值
int a = 5;
//前置递增,变量先加一,再做表达式运算
int b = ++a + 2; //a先加1变成6,然后再参与加2的运算,b = 8
//a = 5;
//后置递增,先进行表达式运算,后让变量+1
int c = a++ + 2; //此时a的值为6,先参与运算加2,把结果赋值给c,c = 8
//然后a再加1变成7
对比一下,你可以发现,c的值在变化。原因是 不管前置还是后置递增或递减,最后都要把原本的值覆盖
也就是说 不论是 a++还是++a
,a的值最后都要被覆盖,只是改变的时机不一样罢了,这也就导致了c的值在变化的原因
看到这里,我想你应该明白了 前置&后置递增与递减 的特殊性
前置&后置递增和递减出现在同一表达式的优先级
接下来我们讨论 当表达式中出现同优先级的情况,以下是代码示例:
int a = 5;
int b = 2;
int c = a++ + b++; //c = 7
a = 5;
b = 2;
int d = a++ + b++ + --a; //d = 10
这是运行的结果
有了前面的知识铺垫之后,我们再看这个表达式是不是就更轻松了一点?
当然,你的答案可能会与结果不同,没关系,我们还是一起来看看他的运算顺序到底是怎么样的
首先我需要了解C++运算符的 结合性,什么是结合性?
结合性是指当一个表达式中有多个相同优先级的运算符时,它们的计算顺序是从左到右还是从右到左,左结合是指从左到右计算,右结合是指从右到左计算
- 例如,赋值运算符
=
是右结合的,所以a = b = c相当于a = (b = c),也就是说,先把c的值赋给b,然后再把b的值赋给a - 例如,加法运算符
+
是左结合的,所以a + b + c相当于(a + b) + c,也就是说,先把a和b相加,然后再和c相加
通过上述这两个例子,我们再次回看最简单的代码:
int a = 5;
int b = ++a + 2;
a = 5;
int c = a++ + 2;
根据结合性理论,我们可以看出:
- 前置递增/递减 属于 右结合
- 后置递增/递减 属于 左结合
其实我个人认为,上述的叫法会让初学者误认为,左/右结合 是 向左/右结合
其实真正应该叫 从左(向右)结合 / 从右(向左)结合更为恰当
也就是说,我们应该把 左结合与右结合 称作 从左结合与从右结合,这样避免了歧义,而且先前的叫法本身也不够严谨
int b = ++a + 2
= (a = a + 1) + 2
= (a = 5 + 1) + 2
= 6 + 2
= 8
a的值变化:5->6->参与表达式计算
---------int a = 5------------
int c = a++ + 2
= a + 2
= 5 + 2
= 7
a的值变化:5->参与表达式计算->6
有了上述的理论基础之后我们再回头看这个代码:
int a = 5;
int b = 2;
int c = a++ + b++; //c = 7
/*
1. 先算后置递增,c = a + b = 7
2. 再算自增:a + 1, b + 1
*/
a = 5;
b = 2;
int d = a++ + b++ + --a; //d = 12
/*
1. 先算后置递增,a = 5 + 1 = 6
2. 再算前置递减:a = 6 - 1 = 5
3. 再开始计算表达式:d = a + b + a = 5 + 2 + 5 = 12
*/
答案和过程是不是显而易见?但是!其实在C++中并不推荐 在表达式中多次修改同一个变量的值。这样容易导致表达式的行为不确定
比如,这段代码我在 Microsoft Visual Studio 2022 Community 上运行的结果为 d=10
,但在线编译网站和 Dev Cpp 上运行的结果为 d=12
我也很不解,所以我发送了反馈提交给了VS的社区 Inconsistent results of C++ post-increment operator in different compilers,发现了相似的问题,微软官方给出的回复是:感谢您的反馈。但这是故意的。这是未定义的行为。根据标准,表达式中的操作序列是不确定的,模仿GCC/Clang的未定义行为解决方案不是MSVC的目标
根据 C++ 标准,表达式的计算顺序是未定义的(Undefined Behavior),也就是说,编译器可以自由地决定表达式中各个操作数的计算顺序。这就意味着,在不同的编译器或编译器设置下,对于同一个表达式,得到的结果可能是不同的,甚至是不可预测的
因此,我们应该:
- 不要在表达式中多次修改同一个变量的值。这样容易导致表达式的行为不确定
- 避免嵌套修改同一个变量的值的操作。例如,a = b = c = 0 这样的操作会使 a、b、c 依次被赋值为 0,但赋值的顺序是未定义的
- 在表达式中显式指定计算顺序。可以使用括号或者将表达式分解为多个语句来指定计算顺序
知乎中的一篇文章也指出了这个问题:关于C,C++表达式求值顺序
四则运算
在 C++ 中,四则运算的优先级遵循常见的数学规则,乘法和除法的优先级高于加法和减法。在进行四则运算时,可以使用括号来显式指定计算的优先级
以下是 C++ 中四则运算的优先级列表,从高到低:
回到开头的例子,我们按照这个表格的优先级计算结果
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int result = a + b * c / d - e;
1. 先计算 b*c = 2*3 = 6
2. 后计算 a + 6 / d - e
3. 再计算 a + 1 - e
4. 结果为:result = 1 + 1 - 5 = -3
由于四则运算 (加减乘除) 的结合性都是 从左向右 的,因此,当出现同一优先级但是顺序不同时,一般都按照 从左到右的顺序顺序进行运算,例如:
int a = 10;
int b = 5;
int result = a / b * 3;
5. 先计算 a/b = 10/5 = 2
6. 后计算 2 * 3
7. 结果为:result = 2*3 = 6
至此,我们常见的运算符优先级就讲到这里了,相信你看完一定有所收获😘,如果有什么问题可以私信或者评论区留言,我看到之后都会回复的
希望这篇文章能让你对C++中运算符优先级有更进一步的认识🤗
例题检测 (理解后可以直接到这里做题)
下面是几个关于 前置&后置递增与递减 的C++例题,希望能更进一步帮助你加深对概念的理解,答案在文章的末尾
题目一:
int a = 3;
int b = ++a + 2;
int c = a++ + 2;
题目二:
int a = 6;
int b = a++ * 3;
a = 6;
int c = --a * 3;
题目3:
int a = 6;
int b = ++a - 2;
int c = a++ / 8;
int d = --a * 3;
int e = a++ + 2;
题目4:
int a = 5;
int b = 6;
int c = (a++) * (++b) + 2;
int d = (++a) * (b--) + 2;
题目5:
int a = 8;
int b = 9;
int c = ++a - (b--) / 2;
int d = a-- - (++b / 2);
题目6
int a = 7;
int b = 8;
int c = (a--) * (b++) + 2;
int d = (--a) * (--b) + 2;
题目1:b = 6, c = 6
题目2:b = 18, c = 15
题目3:b = 5, c = 0, d = 21, e = 9
题目4:c = 37, d = 51
题目5:c = 5, d = 5
题目6:c = 58, d = 42
更多推荐
所有评论(0)