目录

第二章 运算符与表达式

本章将详细介绍C++中的各种运算符和表达式,包括算术运算符、关系运算符、逻辑运算符、位运算符等,以及运算符优先级和结合性的规则。

2.1 算术运算符

2.1.1 基本算术运算符

C++提供了五种基本算术运算符:

运算符 名称 示例 结果
——–——————
+ 加法 5 + 3 8
- 减法 5 - 3 2
* 乘法 5 * 3 15
/ 除法 5 / 3 1(整数除法)
% 取模 5 % 3 2
#include <iostream>
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 <iostream>
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<double>(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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
#include <bitset>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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<int>(d);
    cout << "i = " << i << endl;
 
    // 用于类层次中的上行转换
    Derived derivedObj;
    Base* basePtr = static_cast<Base*>(&derivedObj);
 
    // 2. dynamic_cast - 动态类型转换(用于多态)
    Base* b = new Derived();
    Derived* d2 = dynamic_cast<Derived*>(b);
    if (d2) {
        d2->derivedOnly();
    }
 
    // 3. const_cast - 添加或移除const
    const int x = 10;
    int* p = const_cast<int*>(&x);
    // *p = 20;  // 未定义行为!不要修改const对象
 
    int y = 10;
    const int* cp = &y;
    int* qp = const_cast<int*>(cp);
    *qp = 20;  // 合法,y不是const
 
    // 4. reinterpret_cast - 底层重新解释
    int num = 65;
    char* c = reinterpret_cast<char*>(&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 <iostream>
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 <iostream>
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 <iostream>
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, e1>>e2
 
    // 安全写法
    i = 0;
    int a = ++i;
    int b = i++;
    int c = a + b;
 
    return 0;
}

2.10 逗号运算符

#include <iostream>
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 本章小结

本章我们学习了:

理解运算符的行为和优先级是写出正确、高效C++代码的基础。

练习题

基础练习:

1. 编写程序实现两个数的交换,分别使用:

  1. 临时变量
  2. 算术运算
  3. 异或运算

2. 解释以下表达式的结果:

 <code cpp>
 int a = 5, b = 3;
 int c = a++ + ++b;
 int d = (a > b) ? a++ : ++b;
 </code>

3. 使用位运算实现以下功能:

  1. 判断一个数是否是2的幂
  2. 计算一个数的二进制中1的个数
  3. 交换两个数的特定位

4. 编写程序使用标志位管理文件权限(读、写、执行)

进阶练习:

5. 实现一个函数,不使用+运算符实现两个整数相加(使用位运算)

6. 分析以下代码的问题并修正:

 <code cpp>
 double x = 0.1, y = 0.2;
 if (x + y == 0.3) {
     cout << "Equal" << endl;
 }
 </code>

7. 解释short-circuit evaluation在以下代码中的作用:

 <code cpp>
 if (ptr != nullptr && ptr->value > 0) { }
 if (index >= 0 && index < size && array[index] == target) { }
 </code>

思考题:

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;
}

下一章

继续学习:第三章 控制结构