C++ Primer 第5章:语句
5.1 简单语句
5.1.1 表达式语句与空语句
cpp
// simple_statements.cpp -- 简单语句
#include <iostream>
int main()
{
using namespace std;
// 表达式语句:表达式末尾加分号
int i = 0; // 声明语句
i = 5; // 表达式语句(赋值)
i + 5; // 表达式语句(结果被丢弃,无意义)
cout << i; // 表达式语句(有副作用:输出)
cout << endl;
// 空语句:只有一个分号
; // 空语句(什么都不做)
// 空语句的合法用途:循环体为空时
int val = 0;
while (val++ < 5)
; // 空语句作为循环体(val递增到5)
cout << "val = " << val << endl; // 6
// ⚠️ 多余的分号(空语句)可能导致意外
int x = 5;
if (x > 0); // ⚠️ 这个分号是空语句,if语句到此结束!
cout << "这行总会执行!" << endl; // 不受if控制
return 0;
}
5.1.2 复合语句(块)
cpp
// compound_statement.cpp -- 复合语句
#include <iostream>
int main()
{
using namespace std;
// 复合语句(块):用花括号括起来的语句序列
// 块有自己的作用域
{
int x = 10; // x 只在这个块内有效
cout << "块内 x = " << x << endl;
}
// cout << x; // ❌ 错误:x 已超出作用域
// 块可以为空
{} // 空块,等价于空语句
// if 语句需要块来包含多条语句
int score = 85;
if (score >= 60)
{
cout << "及格" << endl;
cout << "成绩:" << score << endl;
}
// 没有花括号时,只有紧跟的一条语句受控制
if (score >= 90)
cout << "优秀" << endl;
// 下面这行不受 if 控制
cout << "这行总会执行" << endl;
return 0;
}
5.2 语句作用域
cpp
// statement_scope.cpp -- 语句作用域
#include <iostream>
int main()
{
using namespace std;
// 在控制结构中定义的变量,作用域到该语句结束
// if 语句中定义变量
if (int val = 42) // val 在 if 语句内有效
{
cout << "val = " << val << endl;
}
// cout << val; // ❌ 错误:val 超出作用域
// while 语句中定义变量
while (int i = 5) // i 在每次循环开始时初始化
{
cout << "i = " << i << endl;
break; // 避免无限循环
}
// for 语句中定义变量
for (int i = 0; i < 3; i++)
cout << i << " ";
cout << endl;
// cout << i; // ❌ 错误:i 超出作用域
// 实际应用:在条件中定义变量
// 常见于读取输入
int n;
while (cin >> n && n != 0)
{
cout << "读到:" << n << endl;
}
return 0;
}
5.3 条件语句
5.3.1 if 语句
cpp
// if_statement.cpp -- if语句详解
#include <iostream>
#include <string>
int main()
{
using namespace std;
// ===== 基本 if =====
int grade = 75;
if (grade >= 60)
cout << "及格" << endl;
// ===== if-else =====
if (grade >= 60)
cout << "通过" << endl;
else
cout << "未通过" << endl;
// ===== if-else if-else(多分支)=====
if (grade >= 90)
cout << "A(优秀)" << endl;
else if (grade >= 80)
cout << "B(良好)" << endl;
else if (grade >= 70)
cout << "C(中等)" << endl;
else if (grade >= 60)
cout << "D(及格)" << endl;
else
cout << "F(不及格)" << endl;
// ===== 悬空 else 问题 =====
// else 总是与最近的未匹配的 if 配对
int i = 5, j = 10;
// 缩进可能误导,实际上 else 属于内层 if
if (i > 0)
if (j > 0)
cout << "i>0 且 j>0" << endl;
else // 这个 else 属于 if(j>0),不是 if(i>0)!
cout << "i>0 但 j<=0" << endl;
// 使用花括号明确结构(推荐)
if (i > 0)
{
if (j > 0)
cout << "i>0 且 j>0" << endl;
}
else // 现在 else 明确属于 if(i>0)
{
cout << "i<=0" << endl;
}
// ===== 在 if 条件中定义变量 =====
string line;
if (getline(cin, line)) // 读取成功时执行
{
cout << "读取到:" << line << endl;
}
return 0;
}
5.3.2 switch 语句
cpp
// switch_statement.cpp -- switch语句详解
#include <iostream>
int main()
{
using namespace std;
// ===== 基本 switch =====
int day = 3;
switch (day)
{
case 1:
cout << "星期一" << endl;
break;
case 2:
cout << "星期二" << endl;
break;
case 3:
cout << "星期三" << endl;
break;
case 4:
cout << "星期四" << endl;
break;
case 5:
cout << "星期五" << endl;
break;
case 6:
case 7: // 多个 case 共享同一处理(穿透)
cout << "周末" << endl;
break;
default:
cout << "无效的天数" << endl;
break;
}
// ===== case 穿透(fall-through)=====
// 没有 break 时,会继续执行下一个 case
int x = 2;
cout << "\n穿透示例:" << endl;
switch (x)
{
case 1:
cout << "case 1" << endl;
// 没有 break,继续执行
case 2:
cout << "case 2" << endl;
// 没有 break,继续执行
case 3:
cout << "case 3" << endl;
break;
case 4:
cout << "case 4" << endl;
break;
}
// 输出:case 2、case 3(从 case 2 穿透到 case 3)
// ===== switch 中的变量定义 =====
// ⚠️ 在 case 标签之间定义变量需要注意
switch (x)
{
case 1:
{
// 用花括号创建作用域,可以安全定义变量
int val = 10;
cout << "val = " << val << endl;
break;
}
case 2:
{
int val = 20; // 不同作用域,不冲突
cout << "val = " << val << endl;
break;
}
default:
break;
}
// ===== switch 与字符 =====
char ch = 'a';
int vowels = 0, consonants = 0, other = 0;
switch (ch)
{
case 'a': case 'e': case 'i': case 'o': case 'u':
case 'A': case 'E': case 'I': case 'O': case 'U':
vowels++;
break;
default:
if (isalpha(ch))
consonants++;
else
other++;
break;
}
cout << "\n元音:" << vowels
<< " 辅音:" << consonants
<< " 其他:" << other << endl;
return 0;
}
5.4 迭代语句
5.4.1 while 语句
cpp
// while_statement.cpp -- while语句详解
#include <iostream>
#include <string>
#include <vector>
int main()
{
using namespace std;
// ===== 基本 while =====
int i = 0;
while (i < 5)
{
cout << i << " ";
i++;
}
cout << endl;
// ===== 读取输入直到 EOF =====
// while (cin >> val) 是最常见的输入循环
vector<int> nums;
int val;
cout << "输入整数(Ctrl+Z/D结束):" << endl;
while (cin >> val)
nums.push_back(val);
cout << "读取了 " << nums.size() << " 个数" << endl;
cin.clear(); // 清除错误状态
// ===== 哨兵值控制循环 =====
int sum = 0;
int num;
cout << "输入整数(0结束):";
cin >> num;
while (num != 0)
{
sum += num;
cin >> num;
}
cout << "总和:" << sum << endl;
// ===== while 与迭代器 =====
vector<string> words = {"Hello", "World", "C++"};
auto it = words.begin();
while (it != words.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
5.4.2 传统 for 语句
cpp
// for_statement.cpp -- for语句详解
#include <iostream>
#include <vector>
#include <string>
int main()
{
using namespace std;
// ===== 基本 for =====
// for (初始化; 条件; 表达式) 语句
for (int i = 0; i < 5; i++)
cout << i << " ";
cout << endl;
// ===== for 的灵活用法 =====
// 省略初始化
int i = 0;
for (; i < 5; i++)
cout << i << " ";
cout << endl;
// 省略条件(无限循环)
for (int j = 0; ; j++)
{
if (j >= 5) break;
cout << j << " ";
}
cout << endl;
// 省略表达式(在循环体中更新)
for (int k = 0; k < 5; )
{
cout << k << " ";
k += 2; // 步长为2
}
cout << endl;
// 多个变量(逗号运算符)
for (int a = 0, b = 10; a < b; a++, b--)
cout << "a=" << a << " b=" << b << endl;
// ===== for 与 vector =====
vector<int> v = {1, 2, 3, 4, 5};
// 正向遍历
for (size_t i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
// 反向遍历
for (int i = v.size() - 1; i >= 0; i--)
cout << v[i] << " ";
cout << endl;
// ===== 嵌套 for =====
// 打印乘法表
for (int row = 1; row <= 5; row++)
{
for (int col = 1; col <= row; col++)
cout << col << "×" << row << "=" << row*col << "\t";
cout << endl;
}
return 0;
}
5.4.3 范围 for 语句(C++11)
cpp
// range_for.cpp -- 范围for语句
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main()
{
using namespace std;
// ===== 基本范围for =====
// for (声明 : 表达式) 语句
vector<int> v = {1, 2, 3, 4, 5};
// 只读遍历(值拷贝)
for (int x : v)
cout << x << " ";
cout << endl;
// 只读遍历(const引用,推荐)
for (const auto& x : v)
cout << x << " ";
cout << endl;
// 修改元素(引用)
for (auto& x : v)
x *= 2;
cout << "翻倍后:";
for (auto x : v) cout << x << " ";
cout << endl;
// ===== 遍历字符串 =====
string s = "Hello, World!";
int upper = 0, lower = 0;
for (char c : s)
{
if (isupper(c)) upper++;
if (islower(c)) lower++;
}
cout << "大写:" << upper << " 小写:" << lower << endl;
// ===== 遍历数组 =====
int arr[] = {1, 2, 3, 4, 5};
for (int x : arr)
cout << x << " ";
cout << endl;
// ===== 遍历 map(C++17 结构化绑定)=====
map<string, int> scores = {{"张三", 85}, {"李四", 92}, {"王五", 78}};
for (const auto& [name, score] : scores)
cout << name << ": " << score << endl;
// ===== 初始化列表 =====
for (int x : {1, 2, 3, 4, 5})
cout << x << " ";
cout << endl;
// ===== 注意事项 =====
// ⚠️ 不能在范围for中向容器添加/删除元素(迭代器失效)
vector<int> nums = {1, 2, 3};
// for (auto x : nums)
// nums.push_back(x); // ❌ 危险!迭代器失效
return 0;
}
5.4.4 do-while 语句
cpp
// do_while.cpp -- do-while语句
#include <iostream>
#include <string>
int main()
{
using namespace std;
// do-while:先执行循环体,再判断条件
// 循环体至少执行一次
// ===== 基本 do-while =====
int i = 0;
do
{
cout << i << " ";
i++;
} while (i < 5); // 注意末尾的分号!
cout << endl;
// ===== 典型应用:菜单 =====
int choice;
do
{
cout << "\n===== 菜单 =====" << endl;
cout << "1. 选项一" << endl;
cout << "2. 选项二" << endl;
cout << "0. 退出" << endl;
cout << "请选择:";
cin >> choice;
switch (choice)
{
case 1: cout << "执行选项一" << endl; break;
case 2: cout << "执行选项二" << endl; break;
case 0: cout << "退出" << endl; break;
default: cout << "无效选择" << endl;
}
} while (choice != 0);
// ===== 输入验证 =====
int age;
do
{
cout << "请输入年龄(1-150):";
cin >> age;
if (age < 1 || age > 150)
cout << "年龄无效,请重新输入" << endl;
} while (age < 1 || age > 150);
cout << "年龄:" << age << endl;
// ===== do-while 中不能定义变量用于条件 =====
// do {
// ...
// } while (int x = getValue()); // ❌ 错误!
return 0;
}
5.5 跳转语句
5.5.1 break 语句
cpp
// break_statement.cpp -- break语句
#include <iostream>
#include <vector>
#include <string>
int main()
{
using namespace std;
// ===== break 退出循环 =====
// 找第一个能被7整除的数
for (int i = 1; i <= 100; i++)
{
if (i % 7 == 0)
{
cout << "第一个能被7整除的数:" << i << endl;
break; // 找到后立即退出循环
}
}
// ===== break 退出 switch =====
int x = 2;
switch (x)
{
case 1: cout << "one" << endl; break;
case 2: cout << "two" << endl; break; // break 退出 switch
case 3: cout << "three" << endl; break;
}
// ===== 嵌套循环中的 break =====
// break 只退出最内层循环
cout << "\n嵌套循环中的break:" << endl;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (j == 1) break; // 只退出内层循环
cout << "(" << i << "," << j << ") ";
}
cout << endl;
}
// ===== 用 break 实现"找到即停止" =====
vector<string> names = {"Alice", "Bob", "Charlie", "David"};
string target = "Charlie";
bool found = false;
for (const auto& name : names)
{
if (name == target)
{
found = true;
break;
}
}
cout << target << (found ? " 找到了" : " 未找到") << endl;
return 0;
}
5.5.2 continue 语句
cpp
// continue_statement.cpp -- continue语句
#include <iostream>
#include <vector>
int main()
{
using namespace std;
// ===== continue 跳过本次循环 =====
// 输出1到10中的奇数
cout << "奇数:";
for (int i = 1; i <= 10; i++)
{
if (i % 2 == 0)
continue; // 跳过偶数
cout << i << " ";
}
cout << endl;
// ===== continue 在 while 中 =====
int i = 0;
while (i < 10)
{
i++;
if (i % 3 == 0)
continue; // 跳过3的倍数
cout << i << " ";
}
cout << endl;
// ===== continue 在 do-while 中 =====
// ⚠️ 注意:continue 会跳到条件判断处
int j = 0;
do
{
j++;
if (j % 2 == 0)
continue; // 跳到 while(j < 10) 判断
cout << j << " ";
} while (j < 10);
cout << endl;
// ===== 实际应用:过滤无效输入 =====
vector<int> data = {1, -2, 3, -4, 5, 0, -6, 7};
int sum = 0;
for (int val : data)
{
if (val <= 0)
continue; // 跳过非正数
sum += val;
}
cout << "正数之和:" << sum << endl;
return 0;
}
5.5.3 goto 语句
cpp
// goto_statement.cpp -- goto语句(不推荐使用)
#include <iostream>
int main()
{
using namespace std;
// goto 语句:无条件跳转到标签处
// ⚠️ 强烈不推荐使用!会使代码难以理解和维护
int i = 0;
begin: // 标签
if (i < 5)
{
cout << i << " ";
i++;
goto begin; // 跳转到 begin 标签
}
cout << endl;
// goto 的限制:
// 不能跳过变量的初始化
// goto end;
// int x = 5; // ❌ 不能跳过这个初始化
// end:
// 现代C++中,goto 几乎没有合理的使用场景
// 用 break、continue、return 或异常处理代替
return 0;
}
⚠️ goto 的问题:
- 使代码流程难以追踪
- 可能跳过变量初始化
- 现代C++中几乎不需要使用
- 用
break、continue、return或异常处理代替
5.6 try 语句块和异常处理
5.6.1 throw 表达式
cpp
// throw_demo.cpp -- throw表达式
#include <iostream>
#include <stdexcept>
#include <string>
double divide(double a, double b)
{
if (b == 0)
throw std::runtime_error("除数不能为零"); // 抛出异常
return a / b;
}
int getElement(int arr[], int size, int index)
{
if (index < 0 || index >= size)
throw std::out_of_range("索引越界:" + std::to_string(index));
return arr[index];
}
int main()
{
using namespace std;
// throw 可以抛出任何类型
// throw 42; // 抛出 int
// throw "error"; // 抛出 const char*
// throw runtime_error("msg"); // 抛出标准异常(推荐)
// 调用可能抛出异常的函数
try
{
double result = divide(10.0, 0.0);
cout << result << endl;
}
catch (const runtime_error& e)
{
cout << "捕获异常:" << e.what() << endl;
}
return 0;
}
5.6.2 try-catch 语句块
cpp
// try_catch.cpp -- try-catch详解
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
int main()
{
using namespace std;
// ===== 基本 try-catch =====
try
{
// 可能抛出异常的代码
vector<int> v = {1, 2, 3};
cout << v.at(10) << endl; // 抛出 out_of_range
}
catch (const out_of_range& e)
{
cout << "越界错误:" << e.what() << endl;
}
// ===== 多个 catch 块 =====
auto riskyOp = [](int x) {
if (x == 0) throw runtime_error("运行时错误");
if (x < 0) throw invalid_argument("参数无效");
if (x > 100) throw overflow_error("溢出");
return x * 2;
};
for (int val : {5, 0, -1, 200})
{
try
{
int result = riskyOp(val);
cout << "结果:" << result << endl;
}
catch (const invalid_argument& e)
{
cout << "无效参数:" << e.what() << endl;
}
catch (const overflow_error& e)
{
cout << "溢出错误:" << e.what() << endl;
}
catch (const runtime_error& e)
{
cout << "运行时错误:" << e.what() << endl;
}
catch (const exception& e) // 捕获所有标准异常
{
cout << "标准异常:" << e.what() << endl;
}
catch (...) // 捕获所有异常(包括非标准异常)
{
cout << "未知异常" << endl;
}
}
// ===== 异常的重新抛出 =====
cout << "\n===== 重新抛出 =====" << endl;
try
{
try
{
throw runtime_error("原始错误");
}
catch (const runtime_error& e)
{
cout << "内层捕获:" << e.what() << endl;
throw; // 重新抛出当前异常
}
}
catch (const runtime_error& e)
{
cout << "外层捕获:" << e.what() << endl;
}
return 0;
}
5.6.3 标准异常
cpp
// standard_exceptions.cpp -- 标准异常类
#include <iostream>
#include <stdexcept>
#include <string>
/*
标准异常层次结构:
exception
├── bad_alloc (new失败)
├── bad_cast (dynamic_cast失败)
├── bad_typeid (typeid失败)
├── bad_exception
└── logic_error (逻辑错误,可预防)
├── domain_error (定义域错误)
├── invalid_argument(无效参数)
├── length_error (长度错误)
└── out_of_range (越界)
└── runtime_error (运行时错误,难以预防)
├── overflow_error (上溢)
├── underflow_error(下溢)
└── range_error (范围错误)
*/
// 自定义异常(继承标准异常)
class AppError : public std::runtime_error
{
private:
int errorCode;
public:
AppError(const std::string& msg, int code)
: std::runtime_error(msg), errorCode(code) {}
int getCode() const { return errorCode; }
};
int main()
{
using namespace std;
// 使用各种标准异常
auto testException = [](int type) {
switch (type)
{
case 1: throw out_of_range("索引越界");
case 2: throw invalid_argument("参数无效");
case 3: throw runtime_error("运行时错误");
case 4: throw AppError("应用错误", 404);
default: break;
}
};
for (int i = 1; i <= 4; i++)
{
try
{
testException(i);
}
catch (const AppError& e)
{
cout << "应用错误[" << e.getCode() << "]: " << e.what() << endl;
}
catch (const logic_error& e)
{
cout << "逻辑错误:" << e.what() << endl;
}
catch (const runtime_error& e)
{
cout << "运行时错误:" << e.what() << endl;
}
}
// exception 类的接口
try
{
throw runtime_error("测试");
}
catch (const exception& e)
{
// what() 是 exception 类的虚函数
cout << "what(): " << e.what() << endl;
}
return 0;
}
5.7 综合示例:学生成绩管理
cpp
// grade_manager.cpp -- 综合示例:使用各种语句
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <iomanip>
#include <algorithm>
#include <numeric>
struct Student
{
std::string name;
std::vector<double> scores;
double average() const
{
if (scores.empty())
throw std::logic_error(name + " 没有成绩数据");
return std::accumulate(scores.begin(), scores.end(), 0.0)
/ scores.size();
}
char grade() const
{
double avg = average();
if (avg >= 90) return 'A';
if (avg >= 80) return 'B';
if (avg >= 70) return 'C';
if (avg >= 60) return 'D';
return 'F';
}
};
void printReport(const std::vector<Student>& students)
{
using namespace std;
cout << "\n" << string(55, '=') << endl;
cout << left << setw(10) << "姓名"
<< right << setw(8) << "平均分"
<< setw(6) << "等级"
<< setw(10) << "状态" << endl;
cout << string(55, '-') << endl;
int passCount = 0;
double classTotal = 0;
for (const auto& s : students)
{
try
{
double avg = s.average();
char g = s.grade();
classTotal += avg;
string status;
switch (g)
{
case 'A': status = "优秀"; break;
case 'B': status = "良好"; break;
case 'C': status = "中等"; break;
case 'D': status = "及格"; passCount++; break;
default: status = "不及格"; break;
}
if (g != 'F') passCount++;
cout << left << setw(10) << s.name
<< right << setw(8) << fixed << setprecision(1) << avg
<< setw(6) << g
<< setw(10) << status << endl;
}
catch (const logic_error& e)
{
cout << left << setw(10) << s.name
<< " 错误:" << e.what() << endl;
}
}
cout << string(55, '=') << endl;
if (!students.empty())
{
cout << "班级平均分:" << fixed << setprecision(1)
<< classTotal / students.size() << endl;
cout << "通过人数:" << passCount << "/"
<< students.size() << endl;
}
}
int main()
{
using namespace std;
vector<Student> students = {
{"张三", {85, 92, 78, 96}},
{"李四", {70, 65, 72, 68}},
{"王五", {95, 98, 92, 97}},
{"赵六", {55, 48, 60, 52}},
{"钱七", {}} // 没有成绩(测试异常)
};
// 使用范围for和条件语句处理数据
cout << "===== 成绩分析 =====" << endl;
// 找最高分学生
Student* best = nullptr;
double bestAvg = -1;
for (auto& s : students)
{
try
{
double avg = s.average();
if (avg > bestAvg)
{
bestAvg = avg;
best = &s;
}
}
catch (const logic_error&)
{
continue; // 跳过没有成绩的学生
}
}
if (best)
cout << "最高分:" << best->name
<< "(" << fixed << setprecision(1) << bestAvg << "分)" << endl;
// 打印完整报告
printReport(students);
return 0;
}
输出:
===== 成绩分析 =====
最高分:王五(95.5分)
=======================================================
姓名 平均分 等级 状态
-------------------------------------------------------
张三 87.8 B 良好
李四 68.8 D 及格
王五 95.5 A 优秀
赵六 53.8 F 不及格
钱七 错误:钱七 没有成绩数据
=======================================================
班级平均分:76.5
通过人数:3/5
📝 第5章知识点总结
| 知识点 | 核心要点 |
|---|---|
| 空语句 | 只有 ;,合法但要小心 if(x); 这类误用 |
| 复合语句 | 花括号括起的语句序列,有自己的作用域 |
| 语句作用域 | 在 if/while/for 条件中定义的变量,作用域到语句结束 |
| 悬空else | else 总是与最近的未匹配 if 配对,用花括号明确结构 |
| switch穿透 | 没有 break 会继续执行下一个 case,可利用也可能是bug |
| switch变量 | case 标签间定义变量需要用花括号创建作用域 |
| while | 先判断后执行,适合未知次数,while(cin>>val) 读取输入 |
| for | 三部分可省略,多变量用逗号运算符,嵌套for打印表格 |
| 范围for | for(auto& x : container),修改元素必须用引用,不能增删元素 |
| do-while | 先执行后判断,至少执行一次,末尾有分号,适合菜单 |
| break | 退出最内层循环或switch,嵌套循环只退出最内层 |
| continue | 跳过本次循环剩余部分,进入下一次循环 |
| goto | 不推荐使用,不能跳过变量初始化 |
| throw | 抛出异常,可以是任何类型,推荐抛出标准异常类 |
| try-catch | try包裹可能抛出的代码,catch按顺序匹配,...捕获所有 |
| 标准异常 | exception 基类,logic_error/runtime_error 两大分支 |