引言

运算符是编程语言中用于执行各种操作的符号,比如计算两个数的和、比较大小、判断条件真假、对二进制位进行操作等。C++
提供了丰富的运算符,掌握它们是编写程序的基础。
本文将分类介绍 C++ 中最常用的几类运算符:算术运算符关系运算符逻辑运算符位运算符赋值运算符三目运算符。每个知识点都配有代码示例,并通过内存模型帮助你理解运算符在底层如何操作数据。最后通过练习题巩固所学。


1. 算术运算符

用于执行基本的数学运算。

运算符 含义 示例
+ 加法 a + b
- 减法 a - b
* 乘法 a * b
/ 除法 a / b(整数除法会截断)
% 取模(求余数) a % b(操作数需为整数)

代码示例

#include <iostream>

int main() {
    int a = 10, b = 3;
    std::cout << "a + b = " << a + b << std::endl;   // 13
    std::cout << "a - b = " << a - b << std::endl;   // 7
    std::cout << "a * b = " << a * b << std::endl;   // 30
    std::cout << "a / b = " << a / b << std::endl;   // 3(整数除法,小数部分丢失)
    std::cout << "a % b = " << a % b << std::endl;   // 1

    double x = 10.0, y = 3.0;
    std::cout << "x / y = " << x / y << std::endl;   // 3.33333

    return 0;
}

注意事项

  • 整数除法会向零取整(截断小数)。
  • 取模运算要求两个操作数都是整数,且第二个不能为 0。

2. 关系运算符

用于比较两个值,返回布尔值 true(1)或 false(0)。

运算符 含义 示例
== 等于 a == b
!= 不等于 a != b
> 大于 a > b
< 小于 a < b
>= 大于等于 a >= b
<= 小于等于 a <= b

代码示例

#include <iostream>

int main() {
    int a = 5, b = 8;
    std::cout << std::boolalpha;  // 让 bool 输出 true/false 而不是 1/0
    std::cout << "a == b: " << (a == b) << std::endl;
    std::cout << "a != b: " << (a != b) << std::endl;
    std::cout << "a > b:  " << (a > b) << std::endl;
    std::cout << "a < b:  " << (a < b) << std::endl;
    std::cout << "a >= b: " << (a >= b) << std::endl;
    std::cout << "a <= b: " << (a <= b) << std::endl;
    return 0;
}

输出:

a == b: false
a != b: true
a > b:  false
a < b:  true
a >= b: false
a <= b: true

3. 逻辑运算符

用于组合布尔表达式,常用于条件判断。

运算符 含义 示例
&& 逻辑与(AND) cond1 && cond2(两者都为真才真)
|| 逻辑或(OR) cond1 || cond2(至少一个为真则真)
! 逻辑非(NOT) !cond(取反)

代码示例

#include <iostream>

int main() {
    bool sunny = true;
    bool warm = false;

    std::cout << std::boolalpha;
    std::cout << "sunny && warm: " << (sunny && warm) << std::endl;  // false
    std::cout << "sunny || warm: " << (sunny || warm) << std::endl;  // true
    std::cout << "!sunny: " << (!sunny) << std::endl;                // false

    // 短路求值:&& 左边为 false 时不计算右边;|| 左边为 true 时不计算右边
    int x = 0;
    if (x != 0 && 10 / x > 1) {  // 安全,因为 x != 0 为 false,右边不执行
        std::cout << "不会执行" << std::endl;
    }
    return 0;
}

4. 位运算符

直接对整数的二进制位进行操作,效率高,常用于底层编程。

运算符 含义 示例
& 按位与 a & b
| 按位或 a | b
^ 按位异或(不同为1) a ^ b
~ 按位取反(一元) ~a
<< 左移 a << n(相当于乘以 2ⁿ)
>> 右移 a >> n(相当于除以 2ⁿ,向下取整)

代码示例

#include <iostream>
#include <bitset>  // 用于输出二进制形式

int main() {
    unsigned char a = 0b00110011;  // 二进制 00110011,十进制 51
    unsigned char b = 0b11001100;  // 11001100,十进制 204

    std::cout << "a          = " << std::bitset<8>(a) << std::endl;
    std::cout << "b          = " << std::bitset<8>(b) << std::endl;
    std::cout << "a & b      = " << std::bitset<8>(a & b) << std::endl;  // 00000000
    std::cout << "a | b      = " << std::bitset<8>(a | b) << std::endl;  // 11111111
    std::cout << "a ^ b      = " << std::bitset<8>(a ^ b) << std::endl;  // 11111111
    std::cout << "~a         = " << std::bitset<8>(~a) << std::endl;     // 11001100
    std::cout << "a << 2     = " << std::bitset<8>(a << 2) << std::endl; // 11001100
    std::cout << "a >> 1     = " << std::bitset<8>(a >> 1) << std::endl; // 00011001

    return 0;
}

位运算的常见用途

  • 检查某一位是否为1:(num & (1 << pos)) != 0
  • 设置某一位为1:num |= (1 << pos)
  • 清除某一位:num &= ~(1 << pos)
  • 翻转某一位:num ^= (1 << pos)

5. 赋值运算符

除了基本的 = 赋值,C++ 还提供了复合赋值运算符,将运算和赋值合并。

运算符 示例 等价于
= a = b 将 b 的值赋给 a
+= a += b a = a + b
-= a -= b a = a - b
*= a *= b a = a * b
/= a /= b a = a / b
%= a %= b a = a % b
&= a &= b a = a & b
|= a |= b a = a | b
^= a ^= b a = a ^ b
<<= a <<= n a = a << n
>>= a >>= n a = a >> n

代码示例

#include <iostream>

int main() {
    int x = 10;
    x += 5;   // x = 15
    x -= 3;   // x = 12
    x *= 2;   // x = 24
    x /= 4;   // x = 6
    x %= 4;   // x = 2
    std::cout << "x = " << x << std::endl;

    int y = 0b1010;  // 10
    y <<= 1;         // 20
    y |= 0b0001;     // 21
    std::cout << "y = " << y << std::endl;

    return 0;
}

6. 三目运算符(条件运算符)

语法:条件 ? 表达式1 : 表达式2
如果条件为真,返回表达式1的值,否则返回表达式2的值。可以简化简单的 if-else

代码示例

#include <iostream>

int main() {
    int a = 10, b = 20;
    int max = (a > b) ? a : b;   // max = 20
    std::cout << "max = " << max << std::endl;

    // 也可以用于输出
    std::cout << "a " << ((a == b) ? "等于" : "不等于") << " b" << std::endl;

    // 嵌套三目(不推荐过度使用,可读性差)
    int x = 5, y = 5, z = 5;
    int result = (x > y) ? x : ((y > z) ? y : z);  // 求最大值
    std::cout << "result = " << result << std::endl;

    return 0;
}

7. 内存模型讲解

运算符在底层最终被编译为 CPU 指令,操作内存中的数据。我们以几个典型运算符为例,说明内存中的变化。

7.1 算术运算的内存过程

int a = 5;   // 假设 a 在栈地址 0x1000,存储 0x00000005
int b = 3;   // b 在地址 0x1004,存储 0x00000003
int c = a + b;  // 加法
  • CPU 从内存地址 0x1000 和 0x1004 读取两个 4 字节整数到寄存器。
  • 执行加法指令,结果暂存在寄存器(比如 EAX = 8)。
  • 将结果写回内存中 c 的地址(假设 0x1008),存储 0x00000008。

7.2 关系与逻辑运算

关系运算(如 a > b)产生布尔值,在内存中通常用 1(true)或 0(false)表示。逻辑运算 &&|| 具有短路特性:从左到右求值,一旦能确定结果就不再计算右边表达式。短路行为在编译生成的代码中体现为条件跳转指令,不会对未求值的子表达式生成内存访问。

7.3 位运算的内存视图

位运算直接在内存的二进制位上进行操作。例如:

unsigned char x = 0b10110010;  // 0xB2
unsigned char y = x >> 2;      // 右移两位

内存中:

  • x 的字节:1 0 1 1 0 0 1 0
  • 右移两位(逻辑右移,高位补0):0 0 1 0 1 1 0 0 = 0x2C

所有位运算都是按位独立进行的,没有进位或借位。

7.4 赋值运算符

a = b 的本质是将 b 的内存内容拷贝到 a 的内存中。对于基本类型,这是简单的值拷贝。复合赋值如 a += b 相当于先读取 ab 的值,执行加法,再将结果写回 a

7.5 三目运算符

三目运算符不会产生分支预测开销?实际上它会被编译成条件移动指令(cmov)或跳转,取决于优化级别。内存上没有额外存储,只是根据条件选择两个表达式之一的值。


8. 常见错误与避坑

错误示例 问题 正确做法
if (a = 5) 误用赋值代替比较 将 5 赋给 a,条件永远为真 使用 if (a == 5),或写 if (5 == a) 避免误写
整数除法截断未注意 5/2 得到 2 而不是 2.5 使用浮点数:5.0/2.0
取模运算操作数为负数 结果符号取决于实现(C++11 后保证满足 (a/b)*b + a%b == a 尽量避免负数取模,或了解规则
逻辑运算符短路导致未执行的副作用 if (ptr && ptr->value) 安全,但如果写成 `if (ptr ptr->value)` 会求值两边
位运算对有符号整数右移 可能是算术右移(补符号位)或逻辑右移(补0),行为未定义 对无符号类型进行位运算

9. 练习题

题目:编写一个 C++ 程序,完成以下任务:

  1. 定义两个整数 x = 23y = 7,计算并输出:
    • 加法、减法、乘法、整数除法、取模的结果。
    • x > y 的关系结果。
  2. 定义布尔变量 a = true, b = false,输出 a && b, a || b, !a
  3. 定义一个 unsigned char 变量 flags = 0b10101100,使用位运算:
    • 将第 2 位(从0开始)设置为 1。
    • 将第 5 位清除为 0。
    • 翻转第 4 位。
    • 输出每次操作后的二进制形式。
  4. 使用三目运算符判断一个整数 num = -5 是正数、负数还是零,并输出对应的字符串。
  5. 演示复合赋值:int val = 10; 依次执行 val += 5; val *= 2; val /= 3; 输出最终结果。

期望输出示例(格式可自行设计):

算术运算: 23+7=30, 23-7=16, 23*7=161, 23/7=3, 23%7=2
23 > 7: true
逻辑: true && false = false, true || false = true, !true = false
原始flags: 10101100
设置位2: 10101100 -> 10101100 (可能无变化)
清除位5: 10101100 -> 10001100
翻转位4: 10001100 -> 10011100
num = -5 是负数
val: 10 -> 15 -> 30 -> 10

上期参考答案

#include <iostream>
#include <vector>
#include <string>
#include <type_traits>

// 3. 函数返回引用
decltype(auto) forwardReturn(int& x) {
    return x;   // decltype(x) 是 int&,所以返回 int&
}

int main() {
    // 1. auto 测试
    int a = 10;
    const int ca = a;
    int& ra = a;

    auto b = a;      // int
    auto c = ca;     // int(顶层 const 丢弃)
    auto d = ra;     // int(引用丢弃)

    a = 20;
    std::cout << "b = " << b << " (should be 10, copy)" << std::endl;
    std::cout << "c = " << c << " (10)" << std::endl;
    std::cout << "d = " << d << " (10)" << std::endl;

    // 2. decltype 测试
    decltype(ra) e = a;      // int&
    decltype((a)) f = a;     // int&(双括号)
    decltype(a + 0) g;       // int(纯右值)

    e = 30;
    std::cout << "a after modifying via e: " << a << std::endl; // 30

    f = 40;
    std::cout << "a after modifying via f: " << a << std::endl; // 40

    // g 是 int,未初始化(但这里只定义)
    g = 100;

    // 类型检查
    std::cout << std::boolalpha;
    std::cout << "decltype(e) is int& ? " << std::is_same_v<decltype(e), int&> << std::endl;
    std::cout << "decltype(f) is int& ? " << std::is_same_v<decltype(f), int&> << std::endl;
    std::cout << "decltype(g) is int ? " << std::is_same_v<decltype(g), int> << std::endl;

    // 3. decltype(auto) 返回引用
    int val = 100;
    forwardReturn(val) = 200;
    std::cout << "val after forwardReturn assignment: " << val << std::endl; // 200

    // 4. auto 遍历拷贝陷阱
    std::vector<std::string> vec = {"hello", "world", "c++"};
    std::cout << "Using auto (copy):" << std::endl;
    for (auto s : vec) {   // 每次迭代拷贝字符串
        // 如果修改 s,不会影响原vec
    }
    // 验证没有修改原数据,故无需输出地址,只需知道拷贝发生。
    // 正确用法:for (auto& s : vec)

    return 0;
}

总结:运算符是 C++ 程序的基石。算术运算符处理数学计算,关系运算符比较数值,逻辑运算符组合条件,位运算符操控二进制位,赋值运算符简化更新操作,三目运算符提供简洁的条件选择。理解它们的用法和底层内存行为,能帮助你写出更高效、更清晰的代码。下一篇文章我们将学习条件分支(if-elseswitch-case),让程序具备决策能力。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐