C++学习:六个月从基础到就业——C++基础语法回顾:控制流语句
本文是我C++学习之旅系列的第三篇技术文章,主要回顾C++中的控制流语句,包括条件语句、循环语句以及跳转语句,并分享一些实用技巧和最佳实践。查看完整系列目录了解更多内容。
引言
控制流语句是编程语言的核心组成部分,它们决定了程序执行的路径和顺序。无论是简单的判断、重复执行特定代码块,还是在特定条件下跳转到程序的其他部分,控制流语句都扮演着至关重要的角色。本文将详细介绍C++中的各种控制流语句,包括条件语句、循环语句和跳转语句,并提供一些实用的编程技巧和最佳实践。
条件语句
条件语句允许程序根据条件的真假来决定执行哪段代码。C++提供了多种条件语句,包括if语句、if-else语句、if-else if-else语句和switch语句。
if语句
if语句是最基本的条件语句,当条件为真时执行指定的代码块:
if (condition) {// 当condition为真时执行的代码
}
示例:
int age = 20;
if (age >= 18) {std::cout << "您已成年,可以投票。" << std::endl;
}
注意:虽然当代码块只有一行时可以省略花括号,但为了代码的可读性和避免潜在的错误,建议始终使用花括号。
if-else语句
if-else语句在条件为真时执行一个代码块,在条件为假时执行另一个代码块:
if (condition) {// 当condition为真时执行的代码
} else {// 当condition为假时执行的代码
}
示例:
int age = 16;
if (age >= 18) {std::cout << "您已成年,可以投票。" << std::endl;
} else {std::cout << "您未成年,不能投票。" << std::endl;
}
if-else if-else语句
if-else if-else语句用于测试多个条件,并在满足第一个为真的条件时执行相应的代码块:
if (condition1) {// 当condition1为真时执行的代码
} else if (condition2) {// 当condition1为假且condition2为真时执行的代码
} else {// 当所有条件都为假时执行的代码
}
示例:
int score = 85;
if (score >= 90) {std::cout << "优秀" << std::endl;
} else if (score >= 80) {std::cout << "良好" << std::endl;
} else if (score >= 60) {std::cout << "及格" << std::endl;
} else {std::cout << "不及格" << std::endl;
}
嵌套if语句
在C++中,可以在一个if或else语句内部嵌套另一个if或else语句:
if (condition1) {if (condition2) {// 当condition1和condition2都为真时执行的代码} else {// 当condition1为真但condition2为假时执行的代码}
} else {// 当condition1为假时执行的代码
}
示例:
int age = 20;
bool hasID = true;if (age >= 18) {if (hasID) {std::cout << "您可以进入。" << std::endl;} else {std::cout << "请出示您的身份证。" << std::endl;}
} else {std::cout << "未成年人不得入内。" << std::endl;
}
条件运算符(三元运算符)
条件运算符是if-else语句的简写形式,特别适用于简单的条件判断:
condition ? expression1 : expression2;
如果条件为真,整个表达式的值为expression1;如果条件为假,整个表达式的值为expression2。
示例:
int age = 20;
std::string status = (age >= 18) ? "成年" : "未成年";
std::cout << "状态:" << status << std::endl;
嵌套条件运算符:
条件运算符可以嵌套使用,但可能会降低代码的可读性:
int score = 85;
std::string grade = (score >= 90) ? "A" : (score >= 80) ? "B" : (score >= 70) ? "C" : (score >= 60) ? "D" : "F";
在复杂条件判断中,使用if-else if-else语句通常更为清晰。
switch语句
switch语句提供了一种根据表达式的值选择多个代码路径的方法:
switch (expression) {case constant1:// 当expression等于constant1时执行的代码break;case constant2:// 当expression等于constant2时执行的代码break;// 更多case语句default:// 当expression不匹配任何常量时执行的代码break;
}
示例:
int day = 3;
switch (day) {case 1:std::cout << "星期一" << std::endl;break;case 2:std::cout << "星期二" << std::endl;break;case 3:std::cout << "星期三" << std::endl;break;case 4:std::cout << "星期四" << std::endl;break;case 5:std::cout << "星期五" << std::endl;break;case 6:std::cout << "星期六" << std::endl;break;case 7:std::cout << "星期日" << std::endl;break;default:std::cout << "无效的日期" << std::endl;break;
}
注意事项:
- break语句:每个case后面的break语句是必要的,除非您希望执行"贯穿"多个case。没有break语句,控制流将会"贯穿"到下一个case,直到遇到break或switch语句结束。
int day = 3;
switch (day) {case 1:case 2:case 3:case 4:case 5:std::cout << "工作日" << std::endl;break;case 6:case 7:std::cout << "周末" << std::endl;break;default:std::cout << "无效的日期" << std::endl;break;
}
-
default语句:default语句是可选的,但建议始终包含它以处理未预期的情况。
-
表达式类型:switch表达式必须是整数类型(包括枚举)或可以隐式转换为整数类型的值。
-
case常量:case标签必须是常量表达式,不能是变量或函数调用结果。
C++17的增强:带初始化的if和switch语句
从C++17开始,if和switch语句可以包含一个初始化语句,类似于for循环:
if (initialization; condition) {// 代码
}switch (initialization; expression) {// case语句
}
这种语法允许您在条件检查的范围内创建临时变量,提高了代码的局部性和可读性:
// C++17之前
{auto result = getValue();if (result.success()) {useValue(result.value());}
}// C++17
if (auto result = getValue(); result.success()) {useValue(result.value());
}
使用switch的示例:
switch (auto value = getUserInput(); value) {case 1:processOption1(value);break;case 2:processOption2(value);break;default:handleInvalidInput(value);break;
}
循环语句
循环语句允许重复执行代码块,直到满足特定条件。C++提供了几种类型的循环:while循环、do-while循环、for循环和范围for循环(C++11)。
while循环
while循环在条件为真的情况下重复执行代码块:
while (condition) {// 当condition为真时重复执行的代码
}
示例:
int count = 0;
while (count < 5) {std::cout << count << " ";count++;
}
// 输出:0 1 2 3 4
注意:条件在每次迭代开始前检查。如果初始条件为假,循环体一次也不会执行。
do-while循环
do-while循环至少执行一次循环体,然后在条件为真的情况下继续执行:
do {// 至少执行一次的代码
} while (condition);
示例:
int count = 0;
do {std::cout << count << " ";count++;
} while (count < 5);
// 输出:0 1 2 3 4
do-while循环特别适用于至少需要执行一次操作的情况,例如用户输入验证:
int number;
do {std::cout << "请输入一个正数:";std::cin >> number;
} while (number <= 0);
for循环
for循环提供了一种更结构化的循环方式,特别适合已知迭代次数的情况:
for (initialization; condition; update) {// 循环体
}
for循环的执行流程:
- 执行initialization(只执行一次)
- 检查condition(如果为真,执行循环体;如果为假,跳出循环)
- 执行循环体
- 执行update
- 返回步骤2
示例:
for (int i = 0; i < 5; i++) {std::cout << i << " ";
}
// 输出:0 1 2 3 4
for循环的变体:
- 省略初始化:
int i = 0;
for (; i < 5; i++) {std::cout << i << " ";
}
- 省略条件(创建无限循环,需要内部break):
for (int i = 0; ; i++) {if (i >= 5) break;std::cout << i << " ";
}
- 省略更新:
for (int i = 0; i < 5; ) {std::cout << i << " ";i++;
}
- 省略所有部分(创建无限循环):
for (;;) {// 无限循环,需要内部breakif (someCondition) break;
}
- 多个初始化和更新表达式:
for (int i = 0, j = 10; i < 5; i++, j--) {std::cout << i << "," << j << " ";
}
// 输出:0,10 1,9 2,8 3,7 4,6
范围for循环(C++11)
C++11引入了范围for循环,简化了对容器和数组的遍历:
for (declaration : expression) {// 循环体
}
示例:
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {std::cout << num << " ";
}
// 输出:1 2 3 4 5
使用auto关键字可以更灵活地处理不同类型的容器:
std::map<std::string, int> ages = {{"Alice", 25}, {"Bob", 30}, {"Charlie", 35}};
for (const auto& pair : ages) {std::cout << pair.first << ": " << pair.second << std::endl;
}
引用与值的使用:
- 使用引用可以避免不必要的复制,提高性能:
// 使用引用(推荐,避免复制)
for (const auto& num : numbers) {std::cout << num << " ";
}// 使用值(会复制每个元素)
for (auto num : numbers) {std::cout << num << " ";
}
- 如果需要修改元素,使用非const引用:
for (auto& num : numbers) {num *= 2; // 将所有元素乘以2
}
嵌套循环
循环可以嵌套使用,创建多维迭代:
for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {std::cout << "(" << i << "," << j << ") ";}std::cout << std::endl;
}
输出:
(0,0) (0,1) (0,2)
(1,0) (1,1) (1,2)
(2,0) (2,1) (2,2)
跳转语句
跳转语句允许程序改变正常的控制流程,跳到代码的其他部分。
break语句
break语句用于终止最内层的循环或switch语句,并继续执行循环或switch之后的语句:
for (int i = 0; i < 10; i++) {if (i == 5) {break; // 当i等于5时终止循环}std::cout << i << " ";
}
// 输出:0 1 2 3 4
在嵌套循环中,break只会终止包含它的最内层循环:
for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (i == 1 && j == 1) {break; // 只跳出内层循环}std::cout << "(" << i << "," << j << ") ";}std::cout << std::endl;
}
输出:
(0,0) (0,1) (0,2)
(1,0)
(2,0) (2,1) (2,2)
continue语句
continue语句跳过当前迭代的剩余部分,直接进入下一次迭代:
for (int i = 0; i < 10; i++) {if (i % 2 == 0) {continue; // 跳过偶数}std::cout << i << " ";
}
// 输出:1 3 5 7 9
在不同类型的循环中continue的行为:
- 在for循环中,控制流跳到更新表达式,然后检查条件
- 在while和do-while循环中,控制流直接跳到条件检查
goto语句
goto语句提供了无条件跳转到程序中标记位置的能力:
goto label;
// 跳过的代码
label:
// 执行的代码
示例:
int i = 0;
start:
if (i < 5) {std::cout << i << " ";i++;goto start;
}
// 输出:0 1 2 3 4
注意:goto语句通常被认为是不良的编程实践,因为它会使代码的控制流难以理解和维护。在大多数情况下,循环和条件语句是更好的选择。
return语句
return语句终止当前函数的执行,并可选地返回一个值:
int sum(int a, int b) {return a + b; // 返回两数之和并终止函数
}
在void函数中,return可以用来提前终止函数:
void printPositive(int number) {if (number <= 0) {return; // 如果数字不是正数,提前终止函数}std::cout << "正数:" << number << std::endl;
}
控制流的最佳实践
1. 避免深层嵌套
深层嵌套的条件和循环会使代码难以阅读和维护:
// 不好的示例:深度嵌套
if (condition1) {if (condition2) {if (condition3) {// 代码}}
}// 更好的示例:提前返回
if (!condition1) return;
if (!condition2) return;
if (!condition3) return;
// 代码
2. 使用卫语句简化逻辑
卫语句(guard clauses)可以减少嵌套并提高代码的可读性:
// 使用卫语句
bool processOrder(Order order) {if (!order.isValid()) {logError("无效订单");return false;}if (!order.hasInventory()) {logError("库存不足");return false;}// 处理有效订单的代码return true;
}
3. 优化循环
- 将不变的计算移出循环:
// 不好的示例
for (int i = 0; i < vector.size(); i++) { // size()在每次迭代中计算// 代码
}// 更好的示例
const size_t size = vector.size(); // 计算一次
for (int i = 0; i < size; i++) {// 代码
}
- 尽可能使用范围for循环:
// 冗长的传统for循环
for (auto it = container.begin(); it != container.end(); ++it) {std::cout << *it << " ";
}// 更简洁的范围for循环
for (const auto& item : container) {std::cout << item << " ";
}
4. 合理使用switch与if-else
- 当有多个离散值需要比较时,使用switch
- 当需要测试范围或不同条件时,使用if-else
// 适合switch的情况
switch (day) {case 1: /* ... */ break;case 2: /* ... */ break;// ...
}// 适合if-else的情况
if (score >= 90) {// A级
} else if (score >= 80) {// B级
} else {// ...
}
5. 避免神秘数字(magic numbers)
使用命名常量代替硬编码的数字:
// 不好的示例
if (age >= 18) { // 18是什么?为什么是18?// 代码
}// 更好的示例
const int LEGAL_ADULT_AGE = 18;
if (age >= LEGAL_ADULT_AGE) {// 代码
}
6. 使用花括号保持一致性
即使代码块只有一行,也建议使用花括号,以避免潜在的逻辑错误:
// 容易出错
if (condition)doSomething();doSomethingElse(); // 这行总是执行,不受if条件影响// 更清晰、更安全
if (condition) {doSomething();
}
doSomethingElse(); // 明确不在if块内
高级控制流技巧
1. 状态机模式
使用switch语句实现简单的状态机:
enum State { IDLE, RUNNING, PAUSED, STOPPED };State current_state = IDLE;void processEvent(Event event) {switch (current_state) {case IDLE:if (event == START) {current_state = RUNNING;startOperation();}break;case RUNNING:if (event == PAUSE) {current_state = PAUSED;pauseOperation();} else if (event == STOP) {current_state = STOPPED;stopOperation();}break;// 其他状态处理}
}
2. 使用lambda简化控制流
C++11引入的lambda表达式可以简化某些控制流情况:
// 使用lambda处理条件分支
auto process = [&](int value) {if (value < 0) {handleNegative(value);} else if (value > 0) {handlePositive(value);} else {handleZero();}
};// 调用lambda
process(userInput);
3. 表驱动方法
对于复杂的条件逻辑,使用查找表可以简化代码:
// 一个表驱动的日期验证示例
const int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};bool isValidDate(int year, int month, int day) {if (month < 1 || month > 12) return false;// 处理闰年二月int maxDays = daysInMonth[month];if (month == 2 && isLeapYear(year)) {maxDays = 29;}return day >= 1 && day <= maxDays;
}
4. C++17的结构化绑定与控制流
结合C++17的结构化绑定和带初始化的if语句:
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}, {"Charlie", 92}};if (auto [iter, inserted] = scores.insert({"David", 88}); inserted) {std::cout << "添加了新学生" << std::endl;
} else {std::cout << "学生已存在,分数为" << iter->second << std::endl;
}
实际案例分析
菜单驱动程序
#include <iostream>
#include <string>void showMenu() {std::cout << "\n==== 主菜单 ====\n";std::cout << "1. 添加新记录\n";std::cout << "2. 显示所有记录\n";std::cout << "3. 搜索记录\n";std::cout << "4. 删除记录\n";std::cout << "0. 退出\n";std::cout << "请选择操作: ";
}int main() {int choice;bool running = true;while (running) {showMenu();std::cin >> choice;switch (choice) {case 0:std::cout << "感谢使用,再见!\n";running = false;break;case 1:std::cout << "添加新记录功能\n";// 添加记录代码break;case 2:std::cout << "显示所有记录功能\n";// 显示记录代码break;case 3:std::cout << "搜索记录功能\n";// 搜索记录代码break;case 4:std::cout << "删除记录功能\n";// 删除记录代码break;default:std::cout << "无效选择,请重试\n";break;}}return 0;
}
简单游戏循环
#include <iostream>
#include <chrono>
#include <thread>
#include <random>enum class GameState { MENU, PLAYING, PAUSED, GAME_OVER };int main() {GameState state = GameState::MENU;bool running = true;int score = 0;std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(1, 10);while (running) {switch (state) {case GameState::MENU:std::cout << "====游戏菜单====\n";std::cout << "1. 开始游戏\n";std::cout << "2. 退出\n";int choice;std::cin >> choice;if (choice == 1) {state = GameState::PLAYING;score = 0;} else if (choice == 2) {running = false;}break;case GameState::PLAYING:// 简单游戏逻辑std::cout << "\n当前得分: " << score << std::endl;std::cout << "press 'p' to pause, 'q' to quit: ";char input;std::cin >> input;if (input == 'p') {state = GameState::PAUSED;} else if (input == 'q') {state = GameState::GAME_OVER;} else {// 随机增加分数score += dis(gen);}// 检查游戏结束条件if (score > 50) {std::cout << "你赢了!\n";state = GameState::GAME_OVER;}break;case GameState::PAUSED:std::cout << "游戏已暂停,按'c'继续,'q'退出: ";std::cin >> input;if (input == 'c') {state = GameState::PLAYING;} else if (input == 'q') {state = GameState::GAME_OVER;}break;case GameState::GAME_OVER:std::cout << "游戏结束!最终得分: " << score << std::endl;std::cout << "1. 重新开始\n";std::cout << "2. 退出\n";std::cin >> choice;if (choice == 1) {state = GameState::MENU;} else {running = false;}break;}// 简单的帧率控制std::this_thread::sleep_for(std::chrono::milliseconds(100));}std::cout << "感谢游玩!\n";return 0;
}
总结
控制流语句是编程的基本构建块,掌握它们对于编写高效、可读和健壮的C++程序至关重要。本文回顾了C++中的各种控制流语句,包括条件语句(if、if-else、switch)、循环语句(while、do-while、for、范围for)和跳转语句(break、continue、goto、return)。
通过理解这些控制流机制并遵循最佳实践,您可以编写出更加清晰和可维护的代码。随着您编程经验的增长,您将逐渐学会在不同情况下选择最合适的控制流结构,并有效地组合它们以解决复杂的问题。
在下一篇文章中,我们将探讨C++中的函数定义与调用,包括函数重载、默认参数、内联函数和Lambda表达式等主题。
参考资料
- Bjarne Stroustrup. The C++ Programming Language (4th Edition)
- Scott Meyers. Effective C++
- cppreference.com - 语句
- C++ Core Guidelines - 控制流
这是我C++学习之旅系列的第三篇技术文章。查看完整系列目录了解更多内容。