第3章 运算符与表达式
第3章 运算符与表达式
- 使用算术、关系、逻辑运算符进行数据操作
- 理解运算符优先级,避免写出歧义代码
- 掌握赋值、自增自减等常用复合运算符
想象一下,你正在玩一个游戏:最终得分 = 基础分 + 连击奖励 * 难度系数。这个计算过程在C++里,就是由运算符和表达式完成的。运算符是告诉计算机做什么操作的符号(比如加号+),而表达式是由运算符、变量、常量组合起来,能计算出一个值的式子。
3.1 算术运算符:程序的计算器
算术运算符就是你的四则运算工具。除了基础的加减乘除,还有取余运算。
3.1.1 基础算术运算
| 运算符 | 名称 | 示例 | 结果(假设a=10, b=3) |
|---|---|---|---|
+ | 加法 | a + b | 13 |
- | 减法 | a - b | 7 |
* | 乘法 | a * b | 30 |
/ | 除法 | a / b | 3 |
% | 取模(求余) | a % b | 1 |
这里有两个隐藏的坑。第一,整数除法会丢弃小数部分,直接取整。10 / 3的结果是3,不是3.333。想要得到小数结果,至少有一个操作数是浮点类型,比如10.0 / 3。
第二,取模运算符%只能用于整数类型(int, char等),不能用于float或double。我在一个传感器数据处理项目中,需要判断采样序号是否为10的倍数,用sampleIndex % 10 == 0就非常方便。
int score = 100;
int bonus = 20;
double multiplier = 1.5;
int finalScore = score + bonus; // 正确,结果为120
double finalScoreWithMultiplier = (score + bonus) * multiplier; // 正确,结果为180.0
int wrongDivision = score / 30; // 结果为3,小数部分丢失
double correctDivision = score / 30.0; // 结果为3.333...
常见误区
- 混淆整数与浮点数除法:
5 / 2结果是2,不是2.5。想要2.5,必须写成5.0 / 2或5 / 2.0。 - 对浮点数使用取模运算:
10.5 % 3.2会导致编译错误。取模运算的对象必须是整数。 - 忽略除零错误:
int x = 10 / 0;会导致程序运行时崩溃(除零异常)。实际开发中必须检查除数是否为零。
3.2 赋值与复合赋值运算符
等号=不是“等于”,而是“赋值”。它把右边的值计算出来,塞进左边的变量里。这就像把水(值)倒进一个贴了标签(变量名)的杯子。
基础的赋值很简单:int health = 100;。但C++提供了复合赋值运算符,能让代码更简洁。比如health = health + 10;可以写成health += 10;。
所有算术运算符都有对应的复合形式:+=, -=, *=, /=, %=。实际开发中,大部分团队会优先使用复合赋值运算符,因为它意图更清晰,有时编译器还能生成更优的代码。
int coins = 50;
coins += 10; // coins = coins + 10; 现在coins是60
coins -= 25; // coins = coins - 25; 现在coins是35
coins *= 2; // coins = coins * 2; 现在coins是70
coins /= 7; // coins = coins / 7; 现在coins是10 (70/7)
coins %= 3; // coins = coins % 3; 现在coins是1 (10%3)
关键点:赋值表达式本身也有值
赋值操作a = 5本身也是一个表达式,它的值就是赋给a的值,也就是5。这允许你进行连续赋值:int x = y = 10;。执行顺序是从右向左:先把10赋给y,表达式y = 10的值是10,再把这个值赋给x。但为了代码清晰,我建议新手分开写。
3.3 自增与自减运算符
这是C++里非常常用但也容易出错的运算符。++表示加1,--表示减1。它们有前缀(++i)和后缀(i++)两种形式。
用一个生活类比:假设你排队取餐,前缀++i就像“先给我加一个号,我再取餐”,后缀i++就像“我先按现在的号取餐,取完你再给我加一号”。
int count = 5;
int a = ++count; // 前缀:先count加1变成6,再把6赋给a。结果:a=6, count=6
int b = count++; // 后缀:先把count当前值6赋给b,然后count加1变成7。结果:b=6, count=7
编者提示:自增/自减运算符在循环(第4章)和数组遍历(第5章)中会大量使用。理解它们的区别至关重要,否则会导致“差一错误”(off-by-one error),这是新手最常见的bug之一。
工程实践建议
除非在特定表达式中有明确需求,否则我建议单独使用自增/自减运算符,并优先使用前缀形式(++i)。例如:for(int i = 0; i < 10; ++i)。对于内置类型,前缀和后缀性能几乎没有差别,但养成使用前缀的习惯,在后续学习类对象时能避免不必要的临时对象拷贝。
3.4 关系与逻辑运算符:程序做判断
程序需要做选择:“血量是否大于0?”“玩家是否同时按下了跳跃键和方向键?”。这就需要关系运算符和逻辑运算符,它们计算的结果是布尔值(true或false)。
关系运算符
==等于!=不等于>大于<小于>=大于等于<=小于等于
逻辑运算符
&&逻辑与 (AND)||逻辑或 (OR)!逻辑非 (NOT)
关系运算符很简单:10 > 5 为真,5 == 3 为假。逻辑运算符用于组合多个条件。&&要求两边都为真,结果才为真;||只要一边为真,结果就为真;!则取反。
int health = 75;
bool hasKey = true;
bool isDoorLocked = false;
bool isAlive = (health > 0); // true
bool canOpenDoor = hasKey && !isDoorLocked; // true && true = true
bool needWarning = (health < 20) || (health > 80); // false || false = false
关键点:逻辑运算符的“短路求值”
这是C++的一个重要特性。对于&&,如果左边表达式为false,整个结果已经确定为false,右边表达式根本不会执行。对于||,如果左边为true,右边也不会执行。这可以用来安全地编写条件语句。
int *ptr = nullptr;
// 安全的写法:先检查ptr是否非空,再解引用
if (ptr != nullptr && *ptr > 100) { // 如果ptr为空,*ptr > 100 不会执行,避免了崩溃
// ...
}
3.5 运算符优先级与结合性
当表达式里出现多个运算符时,谁先算?1 + 2 * 3结果是7还是9?这由优先级决定。优先级相同时,计算方向由结合性决定(从左到右或从右到左)。
()明确顺序你不需要记住所有优先级。实际开发中,我遵循一个简单原则:不确定就用括号。括号的优先级最高,可以明确指定计算顺序。写出(1 + 2) * 3比让读者去查1 + 2 * 3的优先级要清晰得多。
但有几个最常见的优先级关系你必须知道:
- 算术 > 关系 > 逻辑 > 赋值。例如:
a = b > c && d;先算b > c,再算&& d,最后赋值给a。 - 逻辑非
!的优先级很高,高于关系运算符。所以!a == b等价于(!a) == b,而不是!(a == b)。 - 赋值运算符是右结合。所以
a = b = 5等价于a = (b = 5)。
int x = 5, y = 10, z = 2;
bool result = x + 3 > y && z != 0; // 等价于 ((x + 3) > y) && (z != 0)
// 计算步骤:1. x+3=8, 2. 8>10=false, 3. z!=0=true, 4. false && true = false
// 清晰的做法:加上括号
bool clearResult = ((x + 3) > y) && (z != 0);
动手试一试
打开你的IDE,声明几个变量并尝试组合本章学到的运算符。
- 计算一个半径为5.0的圆的面积(面积 = π * 半径²,π取3.14159)。
- 声明一个整数
counter初始为0。分别用前缀和后缀自增运算符增加它的值,并打印每次操作前后的值,观察区别。 - 模拟一个游戏状态:
int health = 80; bool hasPotion = true; bool isPoisoned = false;。写出判断“玩家可以安全使用药水”(即生命值低于50且拥有药水且未中毒)的表达式,并打印结果。
检验你的理解
- 表达式
int a = 10 / 4;执行后,a的值是多少?如果想得到2.5,代码应该怎么写? - 以下代码片段输出什么?
int i = 5; int j = ++i + i++; cout << "i=" << i << ", j=" << j; - 假设
int x = 5, y = 10;,表达式!x > y是true还是false?为什么?(提示:考虑优先级)
本章小结
- 运算符是执行操作的符号,表达式是计算值的式子。算术运算符处理数值计算,关系与逻辑运算符产生布尔值用于判断。
- 整数除法会截断小数,取模运算
%仅用于整数。除零会导致运行时错误。 - 前缀
++i与后缀i++有本质区别:前缀先增后用,后缀先用后增。在复杂表达式中混用容易出错。 - 逻辑运算符
&&和||具有短路求值特性,这可以用于编写安全的条件检查代码。 - 当表达式存在多个运算符时,使用括号
()明确计算顺序是最清晰、最安全的做法,无需死记优先级表。
微信扫码关注
抖音扫码关注