C++学习:六个月从基础到就业——C++基础语法回顾:控制流语句
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 (;;) {
// 无限循环,需要内部break
if (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++学习之旅系列的第三篇技术文章。查看完整系列目录了解更多内容。