C++ Primer (第五版) 课后习题 Unit5

 

5.1节练习

练习5.1

问题:什么是空语句?什么时候用到它?


答:整个语句中只有一个单独的分号。语法上需要一条语句但逻辑上不需要,应该使用空语句(官方说法)。我理解的是当需要通过执行循环或者判断,但是并不需要执行任何操作时,就可以加上空语句。

练习5.2

问题:什么是块?什么时候会用到块?


答:用花括号括起来的语句和声明序列,一个快就是一个作用域,块也被称为复合语句。语法上需要一条语句,但是逻辑上需要多条语句,应该使用块。

练习5.3

问题:使用逗号运算符重写1.4.1节的while循环,使他不再需要块,观察该邂逅的代码可读性更高还是更低。


    int sum=0,i=50;
    while(i<100)
        sum+=i++;

❌,没有认真读题,它说使用逗号运算符。

    int sum=0,i=50;
    while(i<100)
        sum+=i,i++;

对比一下当时的代码:

    int sum=0,i=50;
    while(i<100){
        sum+=i;
        ++i;        
    }

可读性降低了。

5.2节练习

练习5.4

问题:说明下列例子含义,如果存在问题,试着修改它。


a)while(string::iterator iter!=s.end()){```}

定义一个迭代器,判断是否指向string对象的末尾。

批注:该变量的声明在循环内,因此每循环一次就要重新声明依次iter变量。

b)while(bool status = find(word)){}

将函数find(word)的结果赋值给布尔变量status,如果状态为假,跳出循环。

if(!status){}

如果status状态为false,则运行条件中的块

批注:这里status是声明在while循环内部,条件语句中的status是未定义的。因此需要将if条件语句写到循环内部。

⚠️:这里的作用域通常不易被发现。

5.3.1节练习

练习5.5

问题:写一段程序,使用if else把数字成绩转换成字母成绩


    const vector<string> scores = {"F","D","C","B","A"};
    int a;
    string sc;
    cin>>a;
    if(a<60)
        sc=scores[0];
    else if(a<70)
        sc=scores[1];
    else if(a<80)
        sc=scores[2];
    else if(a<90)
        sc=scores[3];
    else
        sc=scores[4];
    cout<<sc;

练习5.6

问题:改写上一个程序,使用条件运算符,代替if else语句


string sc2;
    sc2=(a<60)? scores[0]:(
                            (a<70)?scores[1]:(
                                                (a<80)?scores[2]:(
                                                                    (a<90)?scores[3]:scores[4]
                                                                    )
                                                )
                             );

练习5.7

问题:改写下列代码中的错误


a)

if(ival1!=ival2)
    ival1=ival2
else ival1=ival2=0

修改后:

if(ival1!=ival2)
    ival1=ival2;
else ival1=ival2=0

b)

if(ival<minval)
    minval=ival;
    occurs=1;

修改后:

if(ival<minval){
    minval=ival;
    occurs=1;
}

c)

if(int ival = get_value())
    cout<<"ival = "<<ival<<endl;
if(!ival)
    cout<<"ival =0\n";

修改后:

int ival;
if( ival = get_value())
    cout<<"ival = "<<ival<<endl;
if(!ival)
    cout<<"ival =0\n";

d)

if(ival=0)
    ival=get_value();

修改后:

if(ival==0)
    ival=get_value();

练习5.8

问题:什么是悬垂else?C++是如何处理else子句的?


既有多个if语句,还有多个else语句,我们如何知道某一个else是匹配给哪个if的问题,就是悬垂else。C++规定else和离它最近的尚未匹配的if匹配。

5.3.2节练习

练习5.9

问题:编写程序,使用一系列if语句统计从cin读入的文本有多少元音字母。


#include <iostream>
using std::cin;
using std::cout;
using std::string;
int main() {
    string uu ;
    cin>>uu;
    int cnt1=0;
    for(auto iter=uu.begin();iter!=uu.end();iter++){
        switch(*iter){
        case 'a':  case 'e':  case 'i':  case 'o': case 'u':
            cnt1++;
            default:
                break;
        }
    }
    cout<<cnt1;
    return 0;
}

练习5.10

我们之前统计的元音字母的程序存在一个问题,如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小谢形式,也统计元音字母的大写形式,也就是说遇到的'a'和‘A' 都应该被统计


答:这个问题我们有两个思路:

  1. 我们可以再在case后面加上五种情况 ‘A' 'E' ...
  2. 我们可以把传入到switch中的字符一律转小写
    switch(tolower(*iter))

    就像这样。

练习5.11

问题:修改统计元音字母的程序,使其能统计空格、制表符和换行符的数量。


思路:试试加上 ' ';'\t';'\n'

经过尝试,发现此法可行,不过在输入上做了点优化。因为string会自动忽略开头的空白,那为了不让开头的空白被忽略,我们使用getline(cin, )这个方法来读取。不过还是想不到换行符要如何输入。

参考:https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch05.md

#include <iostream>

using std::cin; using std::cout; using std::endl;

int main()
{
    unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0, tabCnt = 0, newLineCnt = 0;
    char ch;
    while (cin >> std::noskipws >> ch)  //noskipws(no skip whitespce)
        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 std::cin;
using std::cout;
using std::endl;
int main() {
    int ffcnt=0,flcnt=0,ficnt=0;
    char ch;
    char last='\0';
    while(cin>>ch) {
        switch (ch) {
            case 'f':
                if (last == 'f') {
                    ++ffcnt;
                    break;
                }

            case 'i':
                if (last == 'f') {
                    ++ficnt;
                    break;
                }
            case 'l':
                if (last == 'f') {
                    ++flcnt;
                    break;
                }
            default:;
        }
        last = ch;
    }
    cout<<"the value of ff:"<<ffcnt<<endl;
    cout<<"the value of fi:"<<ficnt<<endl;
    cout<<"the value of fl:"<<flcnt<<endl;
    return 0;
}

这里定义了一个last,将上次的数据存储下来。这样就可以同时统计连续的两个字符了。

分享:我在第一个程序里面,在输入一个字符后,又输入了一个字符。这样容易引起错误,而且第二次的字符没有被统计进去。

练习5.13

问题:指出下列程序中的错误,修改它们:


直接放上修改结果:

a) 因为case如果不加跳出运算,它是顺序执行的。即使统计到a,也会给e还有其他都加1。直接加上跳转语句就好

switch(ch){
    case 'a' :
                aCnt++;
                braek;

    case 'e' :    
                eCnt++;
                break;
              
    default:iouCnt++;
}

b)因为C++规定,不允许跨过变量初始化语句直接跳转到该变量作用域的另一位置。所以在default再对ix进行一次重新定义

    switch (index) {
        case 1 :
            int ix = get_value();
            ivec[ix] = index;
            break;
        default:
            int ix = ivec.size()-1;
            ivec[ix] = index;
    }

c)对于每个例子后面应该是冒号而不是逗号,通常这种情况编译器报错:expected ':' after 'case'

    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 常量必须是整型常量表达式

const unsigned ival=512, jval = 1024,kval = 4096;

5.4.1节练习

练习5.14

问题:编写一段程序,从标准输入中读取若干string对象并查找连续重复出现的单词。所谓连续重复出现的意思是:一个单词后面紧跟这个单词的本身。要求记录连续重复最大次数及对应的单词。如果则样的单词存在,输出重复出现的最大次数,如果不存在,输出一条信息说明任何单词都没有连续出现过。


#include <iostream>
using std::string;
using std::cin;
using std::cout;
using std::endl;
int main() {
    string cal;
    string last;
    string sign;
    int sum=1;
    int big_sum=1;
    while(cin>>cal)
    {
        if(last==cal)
            ++sum;
        else{
            if(big_sum<sum){
                big_sum=sum;
                sign=last;
            }
            last=cal;
            sum=1;
        }
    }
    if(big_sum==1)
        cout<<"任何单词都没有连续出现过"<<endl;
    else
        cout<<sign<<"出现了"<<big_sum<<"次"<<endl;
    return 0;
}

这里可以参考以下这段代码:(来源:https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch05.md)

#include <iostream>
#include <string>

using std::cout; using std::cin; using std::endl; using std::string; using std::pair;

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节练习

练习5.15

问题:说明下列循环的含义并改正其中的错误


a)ix是在for循环中定义的,在外部不能访问,要想访问,必须定义在循环外部

int ix =0;
for(;ix!=sz;++ix){};
if(ix!=sz) ...

b)缺少了一个分号

int ix;
for(;ix!=sz;++ix) {...}

c)因为ix 和 sz 为条件变量,不可以对其同时改变,不然程序会变成一个死循环

for(int ix =0;ix!=sz;++ix){...}

练习5.16

问题:while循环特别适用于那种条件不变、反复执行操作的情况,例如,当未达到文件末尾时不断读取下一个值。 for循环更像是在按步骤迭代,它的索引值在某个范围内一次变化。根据每种循环的习惯各自编写一段程序,然后分别用另一种循环改写。 如果只能使用一种循环,你倾向于哪种?为什么?


    while(cin>>ch){
        cout<<ch;
    }
    for(;cin>>ch;){
        cout<<ch;
    }

比如同时用两种循环写一个读入并且打印出来的程序。如果只能使用一种循环,我会更加倾向使用while

练习5.17

问题:假设有两个包含整数的vector对象,编写一段程序,检验其中一个vector对象是否是另一个的前缀。 为了实现这一目标,对于两个不等长的vector对象,只需挑出长度较短的那个,把它的所有元素和另一个vector对象比较即可。 例如,如果两个vector对象的元素分别是0、1、1、2 和 0、1、1、2、3、5、8,则程序的返回结果为真。


#include <iostream>
#include <vector>
using std::cout;
using std::vector;
int main() {
    vector<int> l1{0,1,1,2};
    vector<int> l2{0,1,1,2,3,5,8};
    for(int i=0;i<l1.size()&&i<l2.size();i++){
        if(l1[i]!=l2[i]) {cout<<"false"; break;}
        if(i==l1.size()-1||i==l2.size()-1) cout<< "true";
    }
    return 0;
}

参考:https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch05.md

#include <iostream>
#include <vector>
using std::vector;
using std::cout;

bool para_vector(vector<int> const &lef,vector<int> const &rig){
    if(lef.size()>rig.size())
        return(para_vector(rig,lef));
    unsigned i;
    for(i=0;i!=lef.size();++i)
        if(lef[i]!=rig[i]) return false;
    return true;
}
int main() {
    vector<int> a1={0,1,2,3,4,1,1,1};
    vector<int> b1={0,1,2,3,4};
    cout<<para_vector(a1,b1);
    return 0;
}

5.4.4节练习

练习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) 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>
using std::string;
using std::cout;
using std::cin;
int main() {
    string str1,str2;
    string rsp;
    do{
        cout<<"输入两个字符串:";
        cin>>str1>>str2;
        cout<<"两个字符串中较短的是:"<<((str1.size()<str2.size())?str1:((str1==str2)?"OMG:一样长":str2))<<"\n";
        cout<<"是否要继续输入y/n";
        cin>>rsp;
    }while(!rsp.empty()&&rsp[0]=='y');
    return 0;
}

5.5.1节练习

练习5.20

问题:编写一段程序,从标准输入中读取string对象的序列直到连续出现两个相同的单词或者所有的单词都读完为止。 使用while循环一次读取一个单词,当一个单词连续出现两次时使用break语句终止循环。 输出连续重复出现的单词,或者输出一个消息说明没有任何单词是连续重复出现的。


#include <iostream>
using std::string;
using std::cout;
using std::cin;
int main() {
    string str,pstr;
    cout<<"Enter the words:\n";
    while(cin>>str&&str!="#q"){
        if(pstr==str) {
            cout<<pstr;
            break;
        }
        pstr=str;
        cout<<"Enter the words input #q to quit\n";
    }
    if(str=="#q") cout<<"没有任何单词是连续重复的\n";
    return 0;
}

另外一种:https://github.com/applenob/Cpp_Primer_Practice/blob/master/excersize/ch05.md

#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;
}

特别说明以下:cin.eof() 如果到达文件结尾,或者遇到文件终止符,返回真。否则返回假。

5.5.2节练习

练习5.21

问题:修改5.5.1节练习题,使其找到的重复单词必须以大写字母开头


#include <iostream>
using std::string;
using std::cout;
using std::cin;
int main() {
    string str,pstr;
    cout<<"Enter the words:\n";
    while(cin>>str&&str!="#q"){
        if(pstr==str) {
            if(pstr[0]>=65&&pstr[0]<=90){cout<<pstr; break;}
            else continue;
        }
        pstr=str;
        cout<<"Enter the words input #q to quit\n";
    }
    if(str=="#q") cout<<"没有任何单词是连续重复的\n";
    return 0;
}

5.5.3节练习

练习5.22

本节的最后一个例子跳回到begin,其实使用循环能更好的完成该任务,重写这段代码,注意不再使用goto语句

begin:
    int sz = get_size();
    if (sz <= 0) {
        goto begin;
    }

sz=getsize();
while(sz<=0){
    sz=getsize()
}

5.6.3节练习

练习5.23

问题:编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果。


#include <iostream>
#include <stdexcept>
using std::cin;
using std::cout;
int main() {
    int a,b;
    cin>>a>>b;
    cout<<a/b;
    return 0;
}

练习5.24

问题:修改你的程序,使得当第二个数是0时抛出异常。先不要设定catch子句,运行程序并真的为除数输入0,看看会发生什么?


答:

#include <iostream>
#include <stdexcept>
using std::cin;
using std::cout;
using std::runtime_error;
int main() {
    int a,b;
    const char* n="0不能为除数";
    cin>>a>>b;
//    cout<<a/b; //直接这样除会发生危险,因为不能确保b不为0
    runtime_error er(n);
    if(b==0)
        throw er;
    cout<<a/b;

    return 0;
}

输出结果:

练习5.25

问题:修改上一题的程序,使用try语句块去捕获异常。catch子句应该为用户输出一条提示信息,询问其是否输入新数并重新执行try语句块的内容。


#include <iostream>
#include <stdexcept>
using std::cin;
using std::cout;
using std::endl;
using std::runtime_error;
int main() {
    int a, b;
    const char *n = "0不能为除数";


    while (cin >> a >> b) {
    try {
        if(b==0)
            throw runtime_error("divisor is 0");
        cout << a / b<<endl; }
    catch (runtime_error err) {
        cout << err.what() << "\n Try Again?Enter y or n\n ";
        char c;
        cin >> c;
        if (!cin || c == 'n')
            break;
    }
}
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值