sairate c34823a3bd docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-06 14:46:58 +08:00

854 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
sort: 5
---
# 语句
## 练习5.1
什么是空语句?什么时候会用到空语句?
解:
空语句是最简单的语句,空语句由一个单独的分号构成。如果在程序的某个地方,语法上需要一条语句但是逻辑上不需要,此时应该使用空语句,空语句什么也不做。
一种常见的情况是,当循环的全部工作在条件部分就可以完成时,我们通常会用到空语句。使用空语句时最好加上注释,从而令代码的阅读者知道这条语句是有意省略内容的。
```cpp
while (cin >> s && s != sought)
;
```
## 练习5.2
什么是块?什么时候会用到块?
解:
块是指用花括号括起来的语句和声明的序列,也称为复合语句。一个块就是一个作用域,在块中引入的名字只能在块内部以及嵌套在块中的子块里访问。如果在
程序的某个地方,语法上需要一条语句, 但是逻辑上需要多条语句,此时应该使用 块。块不需要以分号结束。
例如,循环体必须是一条语句,但是是我们通常需要在循环体内做很多事情,此时就应该把多条语句用花括号括起来,从而把语句序列转变成块。
```cpp
{
// ...
}
```
如果在程序的某个地方,语法上需要一条语句,而逻辑上需要多条语句,此时应该使用块
```cpp
while (val <= 10) {
sum += val;
++val;
}
```
## 练习5.3
使用逗号运算符重写1.4.1节的`while`循环,使它不再需要块,观察改写之后的代码可读性提高了还是降低了。
```cpp
while (val <= 10)
sum += val, ++val;
```
代码的可读性反而降低了。
## 练习5.4
说明下列例子的含义,如果存在问题,试着修改它。
```cpp
(a) while (string::iterator iter != s.end()) { /* . . . */ }
(b) while (bool status = find(word)) { /* . . . */ }
if (!status) { /* . . . */ }
```
解:
- (a) 这个循环试图用迭代器遍历`string`,但是变量的定义应该放在循环的外面,目前每次循环都会重新定义一个变量,明显是错误的。
- (b) 这个循环的`while``if`是两个独立的语句,`if`语句中无法访问`status`变量正确的做法是应该将status定义在`while`之前。
## 练习5.5
写一段自己的程序,使用`if else`语句实现把数字转换为字母成绩的要求。
```cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
int g;
cout <<"请输入成绩:"<<endl;
cin >> g;
if (g < 0 || g > 100) {
cout << "该成绩不合法" <<endl;
return -1;
}
if(g == 100) {
cout << "等级成绩是:" << "A++" << endl;
return -1;
}
if(g < 60) {
cout << "等级成绩是:" << "F" << endl;
return -1;
}
int i = g /10;
int j = g % 10;
string score, level, finalGrade;
if(i == 9)
score = "A";
else if (i == 8)
score = "B";
else if (i == 7)
score = "C";
else
score = "D";
if (j > 7) {
level = "+";
}else if (j < 3) {
level = "-";
}else {
level = "";
}
finalGrade = score + level;
cout << "等级成绩是:" << finalGrade << endl;
return 0;
}
```
## 练习5.6
改写上一题的程序,使用条件运算符代替`if else`语句。
```cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
int g;
cout <<"请输入成绩:"<<endl;
cin >> g;
if (g < 0 || g > 100) {
cout << "该成绩不合法" <<endl;
return -1;
}
if(g == 100) {
cout << "等级成绩是:" << "A++" << endl;
return -1;
}
if(g < 60) {
cout << "等级成绩是:" << "F" << endl;
return -1;
}
int i = g /10;
int j = g % 10;
string score, level, finalGrade;
score = (i == 9) ? "A" :(i == 8) ? "B": (i == 7)? "C":"D";
level = (j > 7) ? "+" : (j < 3)? "-": "";
finalGrade = score + level;
cout << "等级成绩是:" << finalGrade << endl;
return 0;
}
```
## 练习5.7
改写下列代码段中的错误。
```cpp
(a) if (ival1 != ival2)
ival1 = ival2
else
ival1 = ival2 = 0;
(b) if (ival < minval)
minval = ival;
occurs = 1;
(c) if (int ival = get_value())
cout << "ival = " << ival << endl;
if (!ival)
cout << "ival = 0\n";
(d) if (ival = 0)
ival = get_value();
```
解:
- (a) `ival1 = ival2` 后面少了分号。
- (b) 应该用花括号括起来。
- (c) ival是定义在if语句中的变量应定义在两个if语句的外部。
- (d) `if (ival = 0)` 应该改为 `if (ival == 0)`
## 练习5.8
什么是“悬垂else”C++语言是如何处理else子句的
解:
用来描述在嵌套的`if else`语句中,如果`if``else`多时如何处理的问题。C++使用的方法是`else`匹配最近没有配对的`if`
## 练习5.9
编写一段程序,使用一系列`if`语句统计从`cin`读入的文本中有多少元音字母。
解:
```cpp
#include <iostream>
using namespace std;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
cout << "请输入一段文本" << endl;
while (cin >> ch)
{
if (ch == 'a') ++aCnt;
else if (ch == 'e') ++eCnt;
else if (ch == 'i') ++iCnt;
else if (ch == 'o') ++oCnt;
else if (ch == 'u') ++uCnt;
}
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u: \t" << uCnt << '\n'
<< "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt<< endl;
return 0;
}
```
## 练习5.10
我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计元音字母的大写形式,也就是说,新程序遇到'a'和'A'都应该递增`aCnt`的值,以此类推。
解:
```cpp
#include <iostream>
using namespace std;
int main()
{
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
cout << "请输入一段文本" << endl;
while (cin >> ch)
{
switch (ch) {
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'I':
case 'i':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
}
}
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u: \t" << uCnt << '\n'
<< "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt<< endl;
return 0;
}
```
## 练习5.11
修改统计元音字母的程序,使其也能统计空格、制表符、和换行符的数量。
解:
```cpp
#include <iostream>
using namespace std;
int main()
{
unsigned int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
unsigned int spaceCnt = 0, tabCnt = 0,newlineCnt = 0;
char ch;
cout << "请输入一段文本" << endl;
while (cin >> ch)
{
switch (ch) {
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'I':
case 'i':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case ' ':
++spaceCnt;
break;
case '\t':
++tabCnt;
break;
case '\n':
++newlineCnt;
break;
}
}
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u: \t" << uCnt << '\n'
<< "Number of space: \t" << spaceCnt << '\n'
<< "Number of tab: \t" << tabCnt << '\n'
<< "Number of newline: \t" << newlineCnt << '\n'
<< "Total number: \t" << aCnt + eCnt + iCnt + oCnt +uCnt + spaceCnt + tabCnt + newlineCnt<< endl;
return 0;
}
```
## 练习5.12
修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量:`ff``fl``fi`
解:
```cpp
#include <iostream>
using namespace std;
int main()
{
unsigned int flCnt = 0, ffCnt = 0, fiCnt = 0;
char ch, prech= '\0';
cout << "请输入一段文本" << endl;
while (cin >> ch)
{
bool flag = true;
if (prech == 'f') {
switch (ch) {
case 'f':
++ffCnt;
flag = false;
break;
case 'l':
++flCnt;
break;
case 'i':
++flCnt;
break;
}
}
if (!flag) {
prech = '\0';
}else {
prech = ch;}
}
cout << "Number of ff : \t" << ffCnt << '\n'
<< "Number of fi \t" << fiCnt << '\n'
<< "Number of fl: \t" << flCnt << '\n'
<< endl;
return 0;
}
```
## 练习5.13
下面显示的每个程序都含有一个常见的编码错误,指出错误在哪里,然后修改它们。
```cpp
(a) unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a': aCnt++;
case 'e': eCnt++;
default: iouCnt++;
}
(b) unsigned index = some_value();
switch (index) {
case 1:
int ix = get_value();
ivec[ ix ] = index;
break;
default:
ix = ivec.size()-1;
ivec[ ix ] = index;
}
(c) unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1, 3, 5, 7, 9:
oddcnt++;
break;
case 2, 4, 6, 8, 10:
evencnt++;
break;
}
(d) unsigned ival=512, jval=1024, kval=4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch(swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
```
解:
(a) 少了`break`语句。应该为:
```cpp
unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a': aCnt++; break;
case 'e': eCnt++; break;
default: iouCnt++; break;
}
```
(b) 在`default`分支当中,`ix`未定义。应该在外部定义`ix`
```cpp
unsigned index = some_value();
int ix;
switch (index) {
case 1:
ix = get_value();
ivec[ ix ] = index;
break;
default:
ix = static_cast<int>(ivec.size())-1;
ivec[ ix ] = index;
}
```
(c) `case`后面应该用冒号而不是逗号。
```cpp
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1: case 3: case 5: case 7: case 9:
oddcnt++;
break;
case 2: case 4: case 6: case 8: case 0:
evencnt++;
break;
}
```
(d) `case`标签必须是整型常量表达式。
```cpp
const unsigned ival=512, jval=1024, kval=4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch(swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
```
## 练习5.14
编写一段程序,从标准输入中读取若干`string`对象并查找连续重复出现的单词,所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。
例如:如果输入是:
```
how now now now brown cow cow
```
那么输出应该表明单词now连续出现了3次。
解:
```cpp
##include <iostream>
using namespace std;
int main()
{
string currStr, preStr = " ", maxStr;
int currCnt = 1, maxCnt = 0;
while (cin >> currStr) {
//
if (currStr == preStr) {
++currCnt;
if (currCnt > maxCnt) {
maxCnt = currCnt;
maxStr = currStr;
}
}else {
currCnt = 1;
}
preStr = currStr;
}
if (maxCnt >1) {
cout << "出现最多的字符串是 : " << maxStr << ", 次数是:" << maxCnt << endl;
}else {
cout << "每个字符串都出现了一次" << endl;
}
return 0;
}
```
## 练习5.15
说明下列循环的含义并改正其中的错误。
```cpp
(a) for (int ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .
(b) int ix;
for (ix != sz; ++ix) { /* ... */ }
(c) for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
```
解:
应该改为下面这样:
```cpp
(a) int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .
(b) int ix;
for (; ix != sz; ++ix) { /* ... */ }
(c) for (int ix = 0; ix != sz; ++ix) { /*...*/ }
```
## 练习5.16
`while`循环特别适用于那种条件不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。
`for`循环更像是在按步骤迭代,它的索引值在某个范围内一次变化。根据每种循环的习惯各自编写一段程序,然后分别用另一种循环改写。
如果只能使用一种循环,你倾向于哪种?为什么?
解:
```cpp
int i;
while ( cin >> i )
// ...
for (int i = 0; cin >> i;)
// ...
for (int i = 0; i != size; ++i)
// ...
int i = 0;
while (i != size)
{
// ...
++i;
}
```
如果只能用一种循环,我会更倾向使用`while`,因为`while`显得简洁,代码可读性强。
## 练习5.17
假设有两个包含整数的`vector`对象,编写一段程序,检验其中一个`vector`对象是否是另一个的前缀。
为了实现这一目标,对于两个不等长的`vector`对象,只需挑出长度较短的那个,把它的所有元素和另一个`vector`对象比较即可。
例如,如果两个`vector`对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8则程序的返回结果为真。
解:
```cpp
#include <iostream>
#include <vector>
using std::cout; using std::vector;
bool is_prefix(vector<int> const& lhs, vector<int> const& rhs)
{
if(lhs.size() > rhs.size())
return is_prefix(rhs, lhs);
for(unsigned i = 0; i != lhs.size(); ++i)
if(lhs[i] != rhs[i]) return false;
return true;
}
int main()
{
vector<int> l{ 0, 1, 1, 2 };
vector<int> r{ 0, 1, 1, 2, 3, 5, 8 };
cout << (is_prefix(r, l) ? "yes\n" : "no\n");
return 0;
}
```
## 练习5.18
说明下列循环的含义并改正其中的错误。
```cpp
(a) do { // 应该添加花括号
int v1, v2;
cout << "Please enter two numbers to sum:" ;
if (cin >> v1 >> v2)
cout << "Sum is: " << v1 + v2 << endl;
}while (cin);
(b) int ival;
do {
// . . .
} while (ival = get_response()); // 应该将ival 定义在循环外
(c) int ival = get_response();
do {
ival = get_response();
} while (ival); // 应该将ival 定义在循环外
```
## 练习5.19
编写一段程序,使用`do while`循环重复地执行下述任务:
首先提示用户输入两个`string`对象,然后挑出较短的那个并输出它。
解:
```cpp
#include <iostream>
#include <string>
using std::cout; using std::cin; using std::endl; using std::string;
int main()
{
string rsp;
do {
cout << "Input two strings: ";
string str1, str2;
cin >> str1 >> str2;
cout << (str1 <= str2 ? str1 : str2)
<< " is less than the other. " << "\n\n"
<< "More? Enter yes or no: ";
cin >> rsp;
} while (!rsp.empty() && tolower(rsp[0]) == 'y');
return 0;
}
```
## 练习5.20
编写一段程序,从标准输入中读取`string`对象的序列直到连续出现两个相同的单词或者所有的单词都读完为止。
使用`while`循环一次读取一个单词,当一个单词连续出现两次时使用`break`语句终止循环。
输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。
解:
```cpp
#include <iostream>
#include <string>
using std::cout; using std::cin; using std::endl; using std::string;
int main()
{
string read, tmp;
while (cin >> read)
if (read == tmp) break; else tmp = read;
if (cin.eof()) cout << "no word was repeated." << endl; //eof(end of file)判断输入是否结束,或者文件结束符,等同于 CTRL+Z
else cout << read << " occurs twice in succession." << endl;
return 0;
}
```
## 练习5.21
修改5.5.1节练习题的程序,使其找到的重复单词必须以大写字母开头。
解:
```cpp
#include <iostream>
using std::cin; using std::cout; using std::endl;
#include <string>
using std::string;
int main()
{
string curr, prev;
bool no_twice = true;
while (cin >> curr)
{
if (isupper(curr[0]) && prev == curr)
{
cout << curr << ": occurs twice in succession." << endl;
no_twice = false;
break;
}
prev = curr;
}
if (no_twice)
cout << "no word was repeated." << endl;
return 0;
}
```
## 练习5.22
本节的最后一个例子跳回到`begin`,其实使用循环能更好的完成该任务,重写这段代码,注意不再使用`goto`语句。
```cpp
// 向后跳过一个带初始化的变量定义是合法的
begin:
int sz = get_size();
if (sz <= 0) {
goto begin;
}
```
解:
用 for 循环修改的话就是这样
```cpp
for (int sz = get_size(); sz <=0; sz = get_size())
;
```
## 练习5.23
编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。
解:
```cpp
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
int i, j;
cin >> i >> j;
cout << i / j << endl;
return 0;
}
```
## 练习5.24
修改你的程序使得当第二个数是0时抛出异常。先不要设定`catch`子句运行程序并真的为除数输入0看看会发生什么
解:
```cpp
#include <iostream>
#include <stdexcept>
int main(void)
{
int i, j;
std::cin >> i >> j;
if (j == 0)
throw std::runtime_error("divisor is 0");
std::cout << i / j << std::endl;
return 0;
}
```
## 练习5.25
修改上一题的程序,使用`try`语句块去捕获异常。`catch`子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行`try`语句块的内容。
解:
```cpp
#include <iostream>
#include <stdexcept>
using std::cin; using std::cout; using std::endl; using std::runtime_error;
int main(void)
{
for (int i, j; cout << "Input two integers:\n", cin >> i >> j; )
{
try
{
if (j == 0)
throw runtime_error("divisor is 0");
cout << i / j << endl;
}
catch (runtime_error err)
{
cout << err.what() << "\nTry again? Enter y or n" << endl;
char c;
cin >> c;
if (!cin || c == 'n')
break;
}
}
return 0;
}
```