3. 运算符与表达式:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、运算符优先级
运算符,说白了就是C++里用来操作数据的「工具」。我刚开始学的时候,觉得这东西不就是加减乘除嘛,有啥好讲的?结果写了几年代码才发现,真正的高手往往是在运算符上玩出花来的。今天咱们就把这六类运算符掰开揉碎了聊一聊。
3.1 算术运算符
算术运算符就是咱们数学里的那老几样:+、-、*、/、%。不过C++里有个坑,我当年就踩过。
整数除法会截断!
举个例子:7 / 2 在C++里结果是3,不是3.5。为什么?因为两个整数相除,结果还是整数,小数部分直接扔掉。我有个同事曾经因为这个bug排查了一下午——他算平均分的时候,明明应该是82.5分,结果出来82分,学生不干了。
取模运算符 % 也很常用,它返回除法的余数。比如 7 % 2 结果是1。我个人习惯用 % 来判断奇偶性,或者做循环中的周期性操作。
int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int prod = a * b; // 30
int quot = a / b; // 3(注意!不是3.333)
int rem = a % b; // 1
// 想得到小数结果怎么办?
double result = (double)a / b; // 3.3333
小技巧: 如果你需要保留小数,记得把其中一个操作数转成浮点型。我一般习惯把被除数转一下,这样代码读起来更直观。
3.2 关系运算符
关系运算符用来比较两个值的大小关系,返回 true 或 false。说白了就是判断「谁大谁小、等不等」。
| 运算符 | 含义 | 示例 | 结果 |
|---|---|---|---|
| == | 等于 | 5 == 5 | true |
| != | 不等于 | 5 != 3 | true |
| < | 小于 | 3 < 5 | true |
| > | 大于 | 3 > 5 | false |
| <= | 小于等于 | 5 <= 5 | true |
| >= | 大于等于 | 6 >= 5 | true |
注意: 千万别把 == 写成 =!我见过太多新手在 if 条件里写 if (x = 5),结果程序永远进这个分支。因为 x = 5 是赋值表达式,它的值是5,而5在C++里被视为 true。嗯,这个bug特别隐蔽。
3.3 逻辑运算符
逻辑运算符用来组合多个条件。常用的有三个:&&(与)、||(或)、!(非)。
这里有个重要特性叫「短路求值」。什么意思呢?
- 对于
A && B,如果 A 是 false,B 根本不会执行 - 对于
A || B,如果 A 是 true,B 根本不会执行
我曾经在项目里利用这个特性写过一段代码:
// 只有指针不为空时,才访问它的成员
if (ptr != nullptr && ptr->isValid()) {
// 处理数据
}
你想想看,如果 ptr 是空指针,ptr->isValid() 根本不会执行,程序就不会崩溃。这就是短路求值的好处。
3.4 位运算符
位运算符直接操作二进制位。说实话,日常业务代码里用得不多,但在嵌入式、图形学、加密算法这些领域,位运算是神兵利器。
| 运算符 | 含义 | 示例 |
|---|---|---|
| & | 按位与 | 5 & 3 → 1 |
| | | 按位或 | 5 | 3 → 7 |
| ^ | 按位异或 | 5 ^ 3 → 6 |
| ~ | 按位取反 | ~5 → -6 |
| << | 左移 | 5 << 1 → 10 |
| >> | 右移 | 5 >> 1 → 2 |
左移一位相当于乘以2,右移一位相当于除以2。而且位运算比乘除法快得多。我在做性能优化的时候,经常把 x * 8 改成 x << 3,速度能提升不少。
实用场景: 用位运算做标志位管理。比如一个 int 的32个bit,每个bit代表一个开关状态,省内存又高效。
// 用位运算管理权限
int permissions = 0;
permissions |= 1 << 0; // 赋予读权限
permissions |= 1 << 1; // 赋予写权限
// 检查是否有写权限
if (permissions & (1 << 1)) {
// 允许写入
}
3.5 赋值运算符
最基本的赋值就是 =。但C++还提供了一组复合赋值运算符,比如 +=、-=、*=、/=、%= 等等。
我个人习惯用复合赋值,因为代码更简洁,而且有时候编译器能生成更高效的代码。
int x = 10;
x += 5; // 等价于 x = x + 5,结果是15
x *= 2; // 等价于 x = x * 2,结果是30
x %= 7; // 等价于 x = x % 7,结果是2
建议: 能用复合赋值就用复合赋值。代码少写几个字,可读性反而更高。我团队里的代码规范就明确要求:count = count + 1 必须写成 count += 1。
3.6 运算符优先级
优先级这玩意儿,说白了就是「先算谁后算谁」。比如 2 + 3 * 4,先算乘法再算加法,结果是14,不是20。
我给大家一个最实用的建议:记不住优先级就加括号。括号的优先级最高,而且代码读起来一目了然。
不过有几个常见的坑,我提一下:
++和--的优先级很高,比*和/还高- 逻辑运算符
&&和||的优先级比关系运算符低 - 赋值运算符
=的优先级几乎最低,只比逗号运算符高一点
// 优先级示例
int a = 2 + 3 * 4; // 14,不是20
int b = (2 + 3) * 4; // 20,加括号改变优先级
// 容易混淆的例子
int c = 5;
int d = c++ * 2 + 3; // 先取c的值5,再自增,结果是13
// 此时c的值是6
我曾经在代码审查时看到有人写 if (x & 1 == 0),他想判断x是不是偶数。但 == 的优先级比 & 高,所以实际执行的是 x & (1 == 0),也就是 x & 0,结果永远是0。正确的写法是 if ((x & 1) == 0)。你看,括号多重要!
好了,运算符这块咱们就聊到这儿。记住一句话:多写、多练、多踩坑,自然就熟练了。代码写多了,这些优先级啊、特性啊,都会变成肌肉记忆。
公众号:蓝海资料掘金营,微信deep3321