3. 运算符与表达式:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、运算符优先级

运算符,说白了就是C++里用来操作数据的「工具」。我刚开始学的时候,觉得这东西不就是加减乘除嘛,有啥好讲的?结果写了几年代码才发现,真正的高手往往是在运算符上玩出花来的。今天咱们就把这六类运算符掰开揉碎了聊一聊。

C++ 运算符知识体系 运算符与表达式 算术运算符 + - * / % 整数除法注意截断 关系运算符 == != < > 返回 bool 值 逻辑运算符 && || ! 短路求值特性 位运算符 & | ^ ~ << >> 位运算效率极高 赋值运算符 = += -= 等 右结合性 运算符优先级 记不住就加括号

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 关系运算符

关系运算符用来比较两个值的大小关系,返回 truefalse。说白了就是判断「谁大谁小、等不等」。

运算符 含义 示例 结果
== 等于 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