5.1 简单语句
练习5.1:什么是空语句?什么时候会用到空语句?
答:只含有一个单独的分号的语句就是空语句;
当循环的全部工作在条件部分就可以完成时,通常会用到空语句。
练习5.2:什么是块?什么时候会用到块?
答:用花括号括起来的语句和声明的序列,称为块;如果在程序的某个地方,语法上需要一条语句,但是逻辑上需要多条语句,则应该使用块。
练习5.3:使用逗号运算符(参见4.10节,第140页)重写1.4.1节(第10页)的while循环,使它不再需要块,观察改写之后的代码的可读性提高了还是降低了。
#include <iostream>
int main()
{
int sum = 0, val = 1;
while (val <= 10)
sum += val, ++val;//{sum += val; ++val;}
std::cout << "Sum of 1 to 10 inclusive is " << sum << std::endl;
return 0;
}
改写后的版本降低了可读性。
5.2 语句作用域
练习5.4:说明下列例子的含义,如果存在问题,试着修改它。
(a) while (string::iterator iter != s.end()) {/*...*/}
(b) while (bool status = find(word)) {/*...*/}
if(!status) {/*...*/}
答:(a)不合法,修改后:std::string::iterator iter = s.begin(); while (iter != s.end()) { /* . . . * / }
(b)不合法,修改后: bool status = false; while(status = find(word)){/…/} if(!status) {/…/}
5.3 条件语句
5.3.1 if语句
练习5.5:写一段自己的程序,使用if else语句实现把数字成绩转换成字母成绩的要求。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(){
const vector<string> scores = {"F","D","C","B","A","A++"};
string lettergrade;
int grade = 0;
while(cin >> grade){
if(grade < 60) lettergrade = scores[0];
else {
lettergrade = scores[(grade - 50) / 10];
if(grade != 100) {
if(grade % 10 > 7) lettergrade += '+';
else if(grade % 10 < 3) lettergrade += '-';
}
}
cout << lettergrade << endl;
}
return 0;
}
练习5.6:改写上一题的程序,使用条件运算符(参见4.7节,第134页)代替if else语句。
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(){
const vector<string> scores = {"F","D","C","B","A","A++"};
string lettergrade;
int grade = 0;
while(cin >> grade){
string lettergrade = grade < 60 ? scores[0] : scores[(grade - 50) / 10];
lettergrade += (grade == 100 || grade < 60) ? "" : (grade % 10 > 7) ? "+" : (grade % 10 < 3) ? "-" : "";
cout << lettergrade << endl;
}
return 0;
}
练习5.7:改正下列代码段中的错误。
(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 ) 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;
else if(!ival) cout << “ival = 0\n” ;
(d ) if(ival == 0) ival = get_value();
练习5.8:什么是“悬垂else”?C++语言是如何处理else子句的?
答:一个口语术语,指的是如何处理嵌套if语句中if分支多于else分支的情况。
就C++而言,它规定else与离它最近的尚未匹配的if匹配
5.3.1 switch语句
练习5.9:编写一段程序,使用一系列if语句统计从cin读入的文本中有多少元音字母。
#include <iostream>
using namespace std;
int main(){
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
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 << endl;
return 0;
}
练习5.10:我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计大写形式,也就是说,新程序遇到’a’和’A’都应该递增aCnt的值,以此类推。
#include <iostream>
using namespace std;
int main(){
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
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(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << endl;
return 0;
}
练习5.11:修改统计元音字母的程序,使其也能统计空格、制表符和换行符的数量。
#include <iostream>
using namespace std;
int main(){
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0;
char ch;
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(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << '\n'
<< "Number of space: \t" << spaceCnt << '\n'
<< "Number of tab char: \t" << tabCnt << '\n'
<< "Number of new line: \t" << newLineCnt << endl;
return 0;
}
练习5.12:修改统计元音字母的程序,使其能统计以下含有两个字符的字符序列的数量:ff、fl和fi。
#include <iostream>
using namespace std;
int main(){
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, ffCnt = 0, flCnt = 0, fiCnt = 0;;
char ch;
while (cin >> ch)
{
switch (ch)
{
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
if (prech == 'f') ++fiCnt;
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case 'f':
if (prech == 'f') ++ffCnt;
break;
case 'l':
if (prech == 'f') ++flCnt;
break;
}
prech = ch;
}
cout << "Number of vowel a(A): \t" << aCnt << '\n'
<< "Number of vowel e(E): \t" << eCnt << '\n'
<< "Number of vowel i(I): \t" << iCnt << '\n'
<< "Number of vowel o(O): \t" << oCnt << '\n'
<< "Number of vowel u(U): \t" << uCnt << '\n'
<< "Number of ff: \t" << ffCnt << '\n'
<< "Number of fl: \t" << flCnt << '\n'
<< "Number of fi: \t" << fiCnt << endl;
return 0;
}
练习5.13:下面显示的每个程序都含有一个常见的编程错误,指出错误在哪里,然后修改它们。
(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) // Error: 没有break语句
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) // Error: C++语言规定,不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一个位置。
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) // Error: case标签连写语法错误
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1: case 3: case 5: case 7: case 9:
oddcnt++; // oddcnt != oddCnt
break;
case 2: case 4: case 6: case 8: case 0:
evencnt++; // evencnt != evenCnt
break;
}
(d) // Error: case标签必须是整型常量表达式
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.4 迭代语句
5.4.1 while语句
练习5.14:编写一段程序,从标准输入中读取若干string对象并查找连续重复出现的单词。所谓连续重复出现的意思是:一个单词后面紧跟着这个单词本身。要求记录连续重复出现的最大次数以及对应的单词。如果这样的单词存在,输出重复出现的最大次数;如果不存在,输出一条信息说明任何单词都没有连续出现过。例如,如果输入是
how now now now brown cow cow
那么输出应该表明单词now连续出现了3次。
#include <iostream>
#include <string>
using namespace std;
int main()
{
pair<string, int> max_duplicated;
int count = 0;
for (string str, prestr; cin >> str; prestr = str)
{
if (str == prestr) ++count;
else count = 0;
if (count > max_duplicated.second) max_duplicated = { prestr, count };
}
if (max_duplicated.first.empty()) cout << "There's no duplicated string." << endl;
else cout << "the word " << max_duplicated.first << " occurred " << max_duplicated.second + 1 << " times. " << endl;
return 0;
}
5.4.2 传统的for语句
练习5.15:说明下列循环的含义并改正其中的错误。
(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) { /*...*/ }
修改后:
(a) Error: 结束循环后需要使用ix变量,将ix变量定义在循环体外部;
int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */ }
if (ix != sz)
// . . .
(b) Error: 省略init-statement,但是分号必须保留,这表示init-statement是一条空语句;
int ix;
for (;ix != sz; ++ix) { /* ... */ }
(c) Error: 如果程序开始 ix!=sz成立,ix和sz保持同步的自增,接下来将陷入死循环;
for (int ix = 0; ix != sz; ++ix) { /*...*/ }
练习5.16:while循环特别适用于那种条件保持不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。for循环则更像是在按步骤迭代,它的索引值在某个范围内依次变化。根据每种循环的习惯用法各自编写一段程序,然后分别用另一种循环改写。如果只能使用一种循环,你倾向于使用哪种呢?为什么?
// while习惯用法
int i;
while ( cin >> i )
// ...
// for改写
for (int i = 0; cin >> i;)
// ...
// for习惯用法
for (int i = 0; i != size; ++i)
// ...
// while改写
int i = 0;
while (i != size)
{
// ...
++i;
}
答:倾向于使用for,for循环更加简洁明了。
练习5.17:假设有两个包含整数的vector对象,编写一段程序,检验其中一个vector对象是否是另一个的前缀。为了实现这一目标,对于两个不等长的vector对象,只需挑出长度较短的那个,把它的所有元素和另一个vector对象比较即可。例如,如果两个vector对象的元素分别是0、1、1、2和0、1、1、2、3、5、8,则程序的返回结果应该为真。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec1{ 0, 1, 1, 2 };
vector<int> vec2{ 0, 1, 1, 2, 3, 5, 8 };
bool res = true;
if(vec1.size() > vec2.size()){
cout << false << endl;
return 0;
}
for(int i = 0; i < vec1.size(); ++i){
if(vec1[i] != vec2[i]) {
cout << false << endl;
return 0;
}
}
cout << true << endl;
return 0;
}
5.4.3 范围for语句
5.4.4 do-while语句
练习5.18:说明下列循环的含义并改正其中的错误。
(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) do {
// . . .
} while (int ival = get_response());
(c) do {
int ival = get_response();
} while (ival);
修改后:
(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对象,然后挑出较短的那个并输出它。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string rsp;
do {
cout << "Please 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.5 跳转语句
5.5.1 break语句
练习5.20:编写一段程序,从标准输入中读取string对象的序列直到连续出现两个相同的单词或者所有单词都读完为止。使用while循环一次读取一个单词,当一个单词连续出现两次时使用break语句终止循环。输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string read, tmp;
while (cin >> read){
if (read == tmp) break;
else tmp = read;
}
if (cin.eof()) cout << "no word was repeated." << endl;
else cout << read << " occurs twice in succession." << endl;
return 0;
}
5.5.2 continue语句
练习5.21:修改5.5.1节(第171页)练习题的程序,使其找到的重复单词必须以大写字母开头。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string read, tmp;
bool flag = true;
while (cin >> read){
if (isupper(read[0])){
if (read == tmp) {
cout << read << ": occurs twice in succession." << endl;
flag = false;
break;
}
else tmp = read;
}
}
if (flag)
cout << "no word was repeated." << endl;
return 0;
}
5.5.3 goto语句
练习5.22:本节的最后一个例子跳回到begin,其实使用循环能更好地完成该任务。重写这段代码,注意不再使用goto语句。
for (int sz = get_size(); sz <= 0; sz = get_size())
;
5.6 try语句块和异常处理
5.6.1 throw表达式
5.6.2 try语句
5.6.3 goto语句
练习5.23:编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。
#include <iostream>
using namespace std;
int main(){
int val1, val2;
while(cin >> val1 >> val2){
if(val2 == 0) {
cout << "divisor is 0! " << endl;
continue;
}
int ans = 0;
ans = val1 / val2;
cout << "val1 / val2 = " << ans << endl;
}
return 0;
}
练习5.24:修改你的程序,使得当第二个数是0时抛出异常。先不要设定catch子句,运行程序并真的为除数输入0,看看会发生什么?
#include <iostream>
#include <stdexcept>
using namespace std;
int main(){
int val1, val2;
while(cin >> val1 >> val2){
if(val2 == 0) {
throw runtime_error("divisor is 0! ");
continue;
}
int ans = 0;
ans = val1 / val2;
cout << "val1 / val2 = " << ans << endl;
}
return 0;
}
练习5.25:修改上一题的程序,使用try语句块去捕获异常。catch子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行try语句块的内容。
#include <iostream>
#include <stdexcept>
using namespace std;
int main(){
int val1, val2;
while(cin >> val1 >> val2){
int ans = 0;
try{
if(val2 == 0)
throw runtime_error("divisor is 0! ");
ans = val1 / val2;
cout << "val1 / val2 = " << ans << 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;
}