4.4 [等级1] 变量自增与自减运算 (Increment & Decrement)
1. 概念介绍
自增 (++) 和自减 (--) 运算符是 C++ 中的一元运算符,用于将变量的值加 1 或减 1。它们是编程中用于计数的常用快捷方式。 这两种运算符都有两种形式:
- 前缀 (Prefix) :
++i或--i。先将变量的值加/减 1,然后返回新的值。 - 后缀 (Postfix) :
i++或i--。先返回变量的原始值,然后再将变量的值加/减 1。
2. 算法步骤
- 前缀自增
++i: -
- 将
i的值加 1。 - 整个表达式的结果是
i的新值。
- 将
- 后缀自增
i++: -
- 创建一个
i的临时副本,保存其原始值。 - 将
i的值加 1。 - 整个表达式的结果是那个临时副本的值(即
i的原始值)。
- 创建一个
自减运算符 --i 和 i-- 的步骤与此类似,只是操作为减 1。
3. 算法可视化SVG图示
下图清晰地展示了前缀和后缀自增的区别。
4. 核心特性
- 副作用 (Side Effect) : 这两个运算符的主要作用是改变变量自身的值,这被称为副作用。
- 返回值差异: 前缀返回新值,后缀返回旧值。这是它们最关键的区别。
- 优先级: 后缀
++和--的优先级非常高(与函数调用()同级),而前缀的优先级稍低(与!同级)。 - 未定义行为: 在一个表达式中对同一个变量多次使用自增/自减,可能导致未定义行为。例如
i = i++或cout << i++ << i;的结果在不同编译器下可能不同,应避免这种写法。
5. C++代码基础实现
#include <iostream>
int main() {
int i = 5;
int j = 5;
// 前缀自增
int prefix_result = ++i;
std::cout << "Prefix:" << std::endl;
std::cout << " Result: " << prefix_result << std::endl; // 输出 6
std::cout << " i after: " << i << std::endl; // 输出 6
// 后缀自增
int postfix_result = j++;
std::cout << "\nPostfix:" << std::endl;
std::cout << " Result: " << postfix_result << std::endl; // 输出 5
std::cout << " j after: " << j << std::endl; // 输出 6
return 0;
}
6. 优化策略
- 优先使用前缀形式: 对于内置类型(如
int),编译器通常能优化得一样快。但对于复杂类型(如迭代器),前缀形式 (++it) 通常比后缀形式 (it++) 更高效,因为后缀形式需要创建一个临时对象来保存原始值,而前缀形式不需要。养成优先使用前缀形式的习惯是个好做法。 - 单独使用时无差别: 如果只是为了增加变量的值,而不是在表达式中使用其返回值(如
i++;单独一行),那么前缀和后缀没有性能差异。
7. 优缺点
- 优点: 代码更简洁,特别是在循环中(
for (int i=0; i<n; ++i))。 - 缺点: 容易因混淆前缀和后缀的返回值而产生 bug。在复杂表达式中过度使用会严重降低代码可读性,并可能导致未定义行为。
8. 应用场景
- 循环计数器:
for循环的经典用法。 - 迭代器遍历: 在遍历STL容器时,使用
++it来移动迭代器。 - 计数: 简单地对某个事件发生的次数进行加一或减一。
9. 扩展
- 指针运算: 自增/自减运算符也可以用于指针。
ptr++会使指针指向内存中的下一个元素,移动的字节数取决于指针指向的类型大小。 - 重载: C++ 允许为自定义类重载自增和自减运算符,以便让自定义对象(如智能指针、迭代器)支持这种操作。
10. 课后配套练习及答案
练习1: 循环打印使用 for 循环和自增运算符,打印从1到10的整数。
// 答案
#include <iostream>
int main() {
for (int i = 1; i <= 10; ++i) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
练习2: 前缀与后缀的值预测并验证下面代码的输出。
// 答案
#include <iostream>
int main() {
int x = 10;
int y = 10;
std::cout << ++x << std::endl; // 预测:11
std::cout << y++ << std::endl; // 预测:10
std::cout << x << std::endl; // 预测:11
std::cout << y << std::endl; // 预测:11
return 0;
}
练习3: 倒计时使用 while 循环和自减运算符,从10倒数到1。
// 答案
#include <iostream>
int main() {
int count = 10;
while (count > 0) {
std::cout << count << std::endl;
--count;
}
return 0;
}
练习4: 表达式求值预测并验证下面代码的输出。
// 答案
#include <iostream>
int main() {
int a = 3;
int b = 5;
int c = (a++) * b; // a先参与运算(3),再自增
std::cout << "a=" << a << ", c=" << c << std::endl; // 预测:a=4, c=15
return 0;
}
练习5: 复杂表达式求值预测并验证下面代码的输出。
// 答案
#include <iostream>
int main() {
int i = 2;
int j = 8;
int k = (++i) + (j--); // i先自增(3),j先参与运算(8)再自减
std::cout << "i=" << i << ", j=" << j << ", k=" << k << std::endl; // 预测:i=3, j=7, k=11
return 0;
}
11. 相关网络资源推荐
- Increment/decrement operators - cppreference.com
- Prefix vs Postfix in C++ - GeeksforGeeks