====== 第二章 运算符与表达式 ======
本章将详细介绍C++中的各种运算符和表达式,包括算术运算符、关系运算符、逻辑运算符、位运算符等,以及运算符优先级和结合性的规则。
===== 2.1 算术运算符 =====
=== 2.1.1 基本算术运算符 ===
C++提供了五种基本算术运算符:
| 运算符 | 名称 | 示例 | 结果 |
|--------|------|------|------|
| + | 加法 | 5 + 3 | 8 |
| - | 减法 | 5 - 3 | 2 |
| * | 乘法 | 5 * 3 | 15 |
| / | 除法 | 5 / 3 | 1(整数除法) |
| % | 取模 | 5 % 3 | 2 |
#include
using namespace std;
int main() {
int a = 17, b = 5;
cout << "a + b = " << a + b << endl; // 22
cout << "a - b = " << a - b << endl; // 12
cout << "a * b = " << a * b << endl; // 85
cout << "a / b = " << a / b << endl; // 3(整数除法,截断小数)
cout << "a % b = " << a % b << endl; // 2(余数)
// 浮点数除法
double x = 17.0, y = 5.0;
cout << "x / y = " << x / y << endl; // 3.4
return 0;
}
=== 2.1.2 除法的注意事项 ===
#include
using namespace std;
int main() {
// 整数除法 vs 浮点数除法
cout << 17 / 5 << endl; // 3(整数除法)
cout << 17.0 / 5 << endl; // 3.4(浮点数除法)
cout << 17 / 5.0 << endl; // 3.4
cout << static_cast(17) / 5 << endl; // 3.4(显式转换)
// 除以零(未定义行为)
// cout << 5 / 0; // 运行时错误或崩溃
// 取模运算的符号
cout << 17 % 5 << endl; // 2
cout << -17 % 5 << endl; // -2(符号与被除数相同)
cout << 17 % -5 << endl; // 2
cout << -17 % -5 << endl; // -2
return 0;
}
=== 2.1.3 自增和自减运算符 ===
#include
using namespace std;
int main() {
int a = 5, b = 5;
// 前缀形式:先增减,后使用
cout << ++a << endl; // 6(a先变为6,然后输出)
cout << --a << endl; // 5(a先变为5,然后输出)
// 后缀形式:先使用,后增减
cout << b++ << endl; // 5(先输出5,然后b变为6)
cout << b << endl; // 6
cout << b-- << endl; // 6(先输出6,然后b变为5)
cout << b << endl; // 5
// 复杂示例
int x = 3, y = 3;
int result1 = ++x + 2; // x=4, result1=6
int result2 = y++ + 2; // result2=5, y=4
// 不要在同一表达式中多次修改同一变量(未定义行为)
// int bad = ++x + x++; // 未定义行为!
return 0;
}
=== 2.1.4 复合赋值运算符 ===
#include
using namespace std;
int main() {
int a = 10;
a += 5; // 等价于 a = a + 5; a = 15
a -= 3; // 等价于 a = a - 3; a = 12
a *= 2; // 等价于 a = a * 2; a = 24
a /= 4; // 等价于 a = a / 4; a = 6
a %= 4; // 等价于 a = a % 4; a = 2
// 位运算的复合赋值
int b = 0b1010; // 10
b &= 0b1100; // b = b & 0b1100; b = 0b1000 (8)
b |= 0b0001; // b = b | 0b0001; b = 0b1001 (9)
b ^= 0b0010; // b = b ^ 0b0010; b = 0b1011 (11)
b <<= 1; // b = b << 1; b = 0b10110 (22)
b >>= 2; // b = b >> 2; b = 0b101 (5)
return 0;
}
===== 2.2 关系运算符 =====
=== 2.2.1 关系运算符 ===
| 运算符 | 名称 | 示例 | 结果(a=5, b=3) |
|--------|------|------|-----------------|
| == | 等于 | a == b | false |
| != | 不等于 | a != b | true |
| < | 小于 | a < b | false |
| > | 大于 | a > b | true |
| <= | 小于等于 | a <= b | false |
| >= | 大于等于 | a >= b | true |
#include
using namespace std;
int main() {
int a = 5, b = 3;
cout << "a == b: " << (a == b) << endl; // 0 (false)
cout << "a != b: " << (a != b) << endl; // 1 (true)
cout << "a < b: " << (a < b) << endl; // 0
cout << "a > b: " << (a > b) << endl; // 1
cout << "a <= b: " << (a <= b) << endl; // 0
cout << "a >= b: " << (a >= b) << endl; // 1
// 用于条件判断
if (a > b) {
cout << "a is greater than b" << endl;
}
// 比较浮点数(注意精度问题)
double x = 0.1 + 0.2;
double y = 0.3;
// 错误的方式
if (x == y) { // 可能为false
cout << "Equal" << endl;
}
// 正确的方式
const double EPSILON = 1e-9;
if (fabs(x - y) < EPSILON) {
cout << "Approximately equal" << endl;
}
return 0;
}
=== 2.2.2 关系运算符的连写误区 ===
#include
using namespace std;
int main() {
int x = 5;
// 错误:C++不支持数学式的连写比较
// if (1 < x < 10) // 逻辑错误!
// 正确的方式
if (1 < x && x < 10) {
cout << "x is between 1 and 10" << endl;
}
// 解释错误写法的问题
// (1 < x < 10) 被解析为 ((1 < x) < 10)
// 1 < 5 是 true,转换为 1
// 然后 1 < 10 是 true
// 所以即使 x = -100,结果也是 true!
return 0;
}
===== 2.3 逻辑运算符 =====
=== 2.3.1 基本逻辑运算符 ===
| 运算符 | 名称 | 说明 |
|--------|------|------|
| && | 逻辑与 | 两边都为真时结果为真 |
| || | 逻辑或 | 至少一边为真时结果为真 |
| ! | 逻辑非 | 取反 |
真值表:
| a | b | a && b | a || b | !a |
|---|---|--------|--------|-----|
| true | true | true | true | false |
| true | false | false | true | false |
| false | true | false | true | true |
| false | false | false | false | true |
#include
using namespace std;
int main() {
bool a = true, b = false;
cout << "a && b: " << (a && b) << endl; // 0
cout << "a || b: " << (a || b) << endl; // 1
cout << "!a: " << !a << endl; // 0
cout << "!b: " << !b << endl; // 1
// 短路求值
int x = 5;
// && 短路:如果左边为假,右边不执行
if (false && (++x > 0)) {
// 不会执行
}
cout << "x = " << x << endl; // x仍然是5
// || 短路:如果左边为真,右边不执行
if (true || (++x > 0)) {
// 会执行这里
}
cout << "x = " << x << endl; // x仍然是5
return 0;
}
=== 2.3.2 短路求值的应用 ===
#include
using namespace std;
int main() {
int* ptr = nullptr;
int value = 10;
// 安全的空指针检查
// 如果 ptr 为空,不会访问 *ptr
if (ptr != nullptr && *ptr > 0) {
cout << "Value is positive" << endl;
}
// 另一种写法
ptr = &value;
if (ptr && *ptr > 0) { // ptr 转换为 bool 检查是否为空
cout << "Value: " << *ptr << endl;
}
// 设置默认值
int* configValue = nullptr;
int defaultValue = 100;
// 如果 configValue 有效则使用,否则使用默认值
int result = (configValue ? *configValue : defaultValue);
cout << "Result: " << result << endl;
return 0;
}
===== 2.4 位运算符 =====
=== 2.4.1 位运算符 ===
| 运算符 | 名称 | 说明 |
|--------|------|------|
| & | 按位与 | 对应位都为1时结果为1 |
| | | 按位或 | 对应位至少一个为1时结果为1 |
| ^ | 按位异或 | 对应位不同时结果为1 |
| ~ | 按位取反 | 所有位取反 |
| << | 左移 | 位向左移动,右侧补0 |
| >> | 右移 | 位向右移动 |
#include
#include
using namespace std;
int main() {
unsigned int a = 0b1100; // 12
unsigned int b = 0b1010; // 10
cout << "a = " << bitset<4>(a) << " (" << a << ")" << endl;
cout << "b = " << bitset<4>(b) << " (" << b << ")" << endl;
cout << "a & b = " << bitset<4>(a & b) << " (" << (a & b) << ")" << endl; // 1000 (8)
cout << "a | b = " << bitset<4>(a | b) << " (" << (a | b) << ")" << endl; // 1110 (14)
cout << "a ^ b = " << bitset<4>(a ^ b) << " (" << (a ^ b) << ")" << endl; // 0110 (6)
cout << "~a = " << bitset<4>(~a) << " (" << ~a << ")" << endl; // 取反
cout << "a << 1 = " << bitset<4>(a << 1) << " (" << (a << 1) << ")" << endl; // 11000 (24)
cout << "a >> 1 = " << bitset<4>(a >> 1) << " (" << (a >> 1) << ")" << endl; // 0110 (6)
return 0;
}
=== 2.4.2 位运算的实际应用 ===
#include
using namespace std;
// 权限标志位
const int READ = 1 << 0; // 0001 = 1
const int WRITE = 1 << 1; // 0010 = 2
const int EXECUTE = 1 << 2; // 0100 = 4
const int DELETE = 1 << 3; // 1000 = 8
int main() {
// 设置权限
int permission = READ | WRITE; // 添加读和写权限
// 检查权限
if (permission & READ) {
cout << "Can read" << endl;
}
if (permission & EXECUTE) {
cout << "Can execute" << endl; // 不会输出
}
// 添加权限
permission |= EXECUTE; // 添加执行权限
// 移除权限
permission &= ~WRITE; // 移除写权限
// 切换权限
permission ^= READ; // 切换读权限(有则移除,无则添加)
// 快速乘除2的幂
int x = 10;
cout << "x * 8 = " << (x << 3) << endl; // 80
cout << "x / 4 = " << (x >> 2) << endl; // 2
// 判断奇偶
int num = 15;
if (num & 1) {
cout << num << " is odd" << endl;
} else {
cout << num << " is even" << endl;
}
// 交换两个数(不使用临时变量)
int a = 5, b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
cout << "a = " << a << ", b = " << b << endl; // a=3, b=5
return 0;
}
===== 2.5 条件运算符 =====
=== 2.5.1 三元运算符 ===
#include
using namespace std;
int main() {
int a = 10, b = 20;
// 基本用法
int max = (a > b) ? a : b;
cout << "Max: " << max << endl; // 20
// 嵌套使用(可读性较差,谨慎使用)
int score = 85;
char grade = (score >= 90) ? 'A' :
(score >= 80) ? 'B' :
(score >= 70) ? 'C' :
(score >= 60) ? 'D' : 'F';
cout << "Grade: " << grade << endl; // B
// 类型必须兼容或可转换
auto result = true ? 1 : 2.0; // result是double
// 与if-else对比
int x = 10;
// 三元运算符版本
int abs1 = (x >= 0) ? x : -x;
// if-else版本
int abs2;
if (x >= 0) {
abs2 = x;
} else {
abs2 = -x;
}
return 0;
}
===== 2.6 sizeof运算符 =====
#include
using namespace std;
int main() {
// 基本类型的大小
cout << "sizeof(char) = " << sizeof(char) << endl;
cout << "sizeof(short) = " << sizeof(short) << endl;
cout << "sizeof(int) = " << sizeof(int) << endl;
cout << "sizeof(long) = " << sizeof(long) << endl;
cout << "sizeof(long long) = " << sizeof(long long) << endl;
cout << "sizeof(float) = " << sizeof(float) << endl;
cout << "sizeof(double) = " << sizeof(double) << endl;
cout << "sizeof(bool) = " << sizeof(bool) << endl;
// 变量的大小
int arr[10];
cout << "sizeof(arr) = " << sizeof(arr) << endl; // 40(10 * 4)
cout << "sizeof(arr[0]) = " << sizeof(arr[0]) << endl; // 4
// 计算数组元素个数
int n = sizeof(arr) / sizeof(arr[0]);
cout << "Array elements: " << n << endl;
// C++11: sizeof... 获取变参模板参数个数
return 0;
}
===== 2.7 类型转换运算符 =====
=== 2.7.1 C++类型转换 ===
#include
using namespace std;
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {
public:
void derivedOnly() {}
};
int main() {
double d = 3.14;
// 1. static_cast - 静态类型转换
int i = static_cast(d);
cout << "i = " << i << endl;
// 用于类层次中的上行转换
Derived derivedObj;
Base* basePtr = static_cast(&derivedObj);
// 2. dynamic_cast - 动态类型转换(用于多态)
Base* b = new Derived();
Derived* d2 = dynamic_cast(b);
if (d2) {
d2->derivedOnly();
}
// 3. const_cast - 添加或移除const
const int x = 10;
int* p = const_cast(&x);
// *p = 20; // 未定义行为!不要修改const对象
int y = 10;
const int* cp = &y;
int* qp = const_cast(cp);
*qp = 20; // 合法,y不是const
// 4. reinterpret_cast - 底层重新解释
int num = 65;
char* c = reinterpret_cast(&num);
// C++11新增
// 5. static_pointer_cast, dynamic_pointer_cast, const_pointer_cast - 用于智能指针
delete b;
return 0;
}
===== 2.8 运算符优先级 =====
=== 2.8.1 优先级表 ===
| 优先级 | 运算符 | 说明 | 结合性 |
|--------|--------|------|--------|
| 1 | :: | 作用域解析 | 左到右 |
| 2 | () [] -> . ++ -- | 后缀 | 左到右 |
| 3 | ++ -- + - ! ~ * & sizeof | 前缀 | 右到左 |
| 4 | .* ->* | 成员指针 | 左到右 |
| 5 | * / % | 乘除模 | 左到右 |
| 6 | + - | 加减 | 左到右 |
| 7 | << >> | 位移 | 左到右 |
| 8 | < <= > >= | 关系 | 左到右 |
| 9 | == != | 相等 | 左到右 |
| 10 | & | 按位与 | 左到右 |
| 11 | ^ | 按位异或 | 左到右 |
| 12 | | | 按位或 | 左到右 |
| 13 | && | 逻辑与 | 左到右 |
| 14 | || | 逻辑或 | 左到右 |
| 15 | ?: | 条件 | 右到左 |
| 16 | = += -= 等 | 赋值 | 右到左 |
| 17 | , | 逗号 | 左到右 |
=== 2.8.2 优先级示例 ===
#include
using namespace std;
int main() {
int a = 5, b = 3, c = 2;
// 乘除优先于加减
int result1 = a + b * c; // 5 + 6 = 11,不是 16
// 赋值优先级很低
int x, y;
x = y = 10; // 先 y = 10,然后 x = y
// 关系运算符优先于逻辑运算符
bool result2 = a > b && b > c; // (a > b) && (b > c)
// 位运算符优先级容易出错
int flags = 0b1010;
if (flags & 0b1000 == 0b1000) {
// 错误!相当于 flags & (0b1000 == 0b1000)
// 即 flags & 1
}
if ((flags & 0b1000) == 0b1000) {
// 正确
}
// 条件运算符
int max = a > b ? a : b;
// 逗号运算符
int i = (1, 2, 3); // i = 3
// 建议使用括号明确优先级
int complex = ((a + b) * c - a) / b;
return 0;
}
===== 2.9 表达式求值 =====
=== 2.9.1 左值和右值 ===
#include
using namespace std;
int global = 10;
int getValue() {
return 10; // 返回临时值(右值)
}
int& getGlobal() {
return global; // 返回引用(左值)
}
int main() {
int x = 10;
int y = 20;
// 左值(lvalue):有身份,可取地址
x = 15; // x是左值
&x; // 可以取地址
// 右值(rvalue):临时值,不可取地址
// 10 = x; // 错误!10是右值
// &10; // 错误!
// 左值引用
int& ref = x; // 正确,绑定到左值
// int& ref2 = 10; // 错误,不能绑定到右值
// 右值引用(C++11)
int&& rref = 10; // 正确,绑定到右值
int&& rref2 = x + y; // 正确
// 应用场景
getGlobal() = 20; // 正确,返回左值
// getValue() = 20; // 错误,返回右值
return 0;
}
=== 2.9.2 求值顺序 ===
#include
using namespace std;
int f1() {
cout << "f1" << endl;
return 1;
}
int f2() {
cout << "f2" << endl;
return 2;
}
int f3() {
cout << "f3" << endl;
return 3;
}
int main() {
// 函数参数的求值顺序未指定
// 可能是 f1, f2, f3 或任何顺序
cout << f1() + f2() + f3() << endl;
// 以下代码有未定义行为
int i = 0;
// int x = ++i + i++; // 未定义行为!
// C++17起,某些运算符的求值顺序确定:
// e1.e2, e1->e2, e1[e2], e1<>e2
// 安全写法
i = 0;
int a = ++i;
int b = i++;
int c = a + b;
return 0;
}
===== 2.10 逗号运算符 =====
#include
using namespace std;
int main() {
int a, b, c;
// 逗号表达式:从左到右求值,结果为最后一个表达式的值
c = (a = 1, b = 2, a + b); // c = 3
cout << "c = " << c << endl;
// 在for循环中使用
for (int i = 0, j = 10; i < j; i++, j--) {
cout << "i=" << i << ", j=" << j << endl;
}
// 注意:函数参数中的逗号是分隔符,不是运算符
// func(a, b); // 两个参数
// func((a, b)); // 一个参数,值为b
return 0;
}
===== 2.11 本章小结 =====
本章我们学习了:
* 算术运算符及其特殊注意事项(整数除法、取模)
* 关系运算符和常见错误
* 逻辑运算符及其短路求值特性
* 位运算符及其在标志位、优化计算中的应用
* 条件运算符(三元运算符)的用法
* sizeof运算符获取类型和对象大小
* C++四种类型转换运算符
* 运算符优先级和结合性
* 左值与右值的概念
* 表达式求值顺序的重要性
理解运算符的行为和优先级是写出正确、高效C++代码的基础。
===== 练习题 =====
**基础练习:**
1. 编写程序实现两个数的交换,分别使用:
- 临时变量
- 算术运算
- 异或运算
2. 解释以下表达式的结果:
int a = 5, b = 3;
int c = a++ + ++b;
int d = (a > b) ? a++ : ++b;
3. 使用位运算实现以下功能:
- 判断一个数是否是2的幂
- 计算一个数的二进制中1的个数
- 交换两个数的特定位
4. 编写程序使用标志位管理文件权限(读、写、执行)
**进阶练习:**
5. 实现一个函数,不使用+运算符实现两个整数相加(使用位运算)
6. 分析以下代码的问题并修正:
double x = 0.1, y = 0.2;
if (x + y == 0.3) {
cout << "Equal" << endl;
}
7. 解释short-circuit evaluation在以下代码中的作用:
if (ptr != nullptr && ptr->value > 0) { }
if (index >= 0 && index < size && array[index] == target) { }
**思考题:**
8. 为什么C++的运算符优先级设计成这样?分析几个设计决策。
9. 研究volatile关键字对运算符行为的影响。
10. C++11引入的右值引用如何改变表达式的分类?
===== 参考答案 =====
**练习1参考(异或交换):**
void swap(int& a, int& b) {
if (&a != &b) { // 必须检查是否同一地址
a ^= b;
b ^= a;
a ^= b;
}
}
**练习3参考(判断2的幂):**
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
**练习5参考(位运算加法):**
int add(int a, int b) {
while (b != 0) {
int carry = a & b; // 计算进位
a = a ^ b; // 无进位加法
b = carry << 1; // 进位左移
}
return a;
}
===== 下一章 =====
继续学习:[[第三章_控制结构|第三章 控制结构]]