【C++ 运算符】算术、关系、逻辑、位运算、赋值、三目运算符
引言
运算符是编程语言中用于执行各种操作的符号,比如计算两个数的和、比较大小、判断条件真假、对二进制位进行操作等。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 相当于先读取 a 和 b 的值,执行加法,再将结果写回 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++ 程序,完成以下任务:
- 定义两个整数
x = 23和y = 7,计算并输出:
- 加法、减法、乘法、整数除法、取模的结果。
x > y的关系结果。- 定义布尔变量
a = true,b = false,输出a && b,a || b,!a。- 定义一个
unsigned char变量flags = 0b10101100,使用位运算:
- 将第 2 位(从0开始)设置为 1。
- 将第 5 位清除为 0。
- 翻转第 4 位。
- 输出每次操作后的二进制形式。
- 使用三目运算符判断一个整数
num = -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-else 和 switch-case),让程序具备决策能力。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)