C++ Primer 5th 第5章 语句

和大多数语言一样,C++提供了条件执行语句、重复执行相同代码的循环语句和由于中断当前控制流的跳转语句,表达式语句和声明语句等。

语句有简单语句和复合语句之分。简单语句但多数以分号结束,最简单的语句就是空语句,空语句中就只含有一个单独的分号。

复合语句是用花括号括起来的语句或声明,复合语句也叫块。复合语句不同于简单语句的是,复合语句具有作用域,即块作用域,块中引入的名字只能被当前块或者当前块的字块访问。复合语句是不用分号结尾的。

 

可以在if、switch、while、for语句的控制结构内定义变量,这个变量只能在当前语句内访问,语句结束,变量就消亡了。

 

if语句的语法形式是

  if (condition)

    statement

对于if的condition,必须使用括号括起来,condition可以是一个表达式,也可以是一个初始化了的变量声明,并且不允许为空。

 

switch语句的语法形式是

  switch(expression)

    {

      case value:

    }

对于switch语句,首先对expression表达式求值,expression也可以是一个初始化了的变量声明。表达式的值转换成整形。然后与每个case标签比较。

case标签的值必须是个常量表达式,且是整型类型的。

如果没有任何一个case标签能匹配switch表达式的值,在switch语句有default表达式的情况下,将默认执行该default表达式,default表达式的位置无关紧要。

 

while语句的语法形式是

  while (condition)

    statement

condition是不允许为空的,可以是个表达式,也可以是一个初始化了的变量声明。

while循环不同于for循环的一点是,while条件部分或者while循环体内定义的变量每次迭代后都会被销毁。

 

范围for语句的语法形式是

  for (declaration : expression)

    statement

expression必须是一个序列,比如花括号括起来的初始值列表{1,2,3,4,5},或者数组、vector、string等类型的对象,这些对象的特点是可以使用begin和end成员或者函数来获取其相应的迭代器。

 

do while语句中while的条件部分使用的变量必须是循环前定义的。 

 

break语句用于终止循环,break语句只能出现在迭代语句或者switch语句的内部,嵌套在循环语句的子语句也是可以的。

continue语句用于停止当前迭代的执行,重新开始下一次的迭代,但是不能终止循环,continue语句只能出现在for、while、do while循环的内部,或者循环内部的子块。

goto语句较难把控,不建议使用

 

try语句块和异常处理:

当程序的某部分检测到一个它无法处理的问题时,需要使用异常处理,异常分为两块,一块是异常的检测,一块是异常的处理。

 检测部分是发出某种信号,表明遇到故障。发出是由throw完成的,称作throw引发了异常。某种信号则是异常的类型。

异常的处理是try和catch负责的。

throw表达式引发一个异常,表达式包含关键字和一个表达式,表达式的类型就是异常的类型,用于发出某种信号。

try语句块是一个关键字加上紧随的复合语句块,必须是复合语句块,不能是一个简单语句。

try语句之后是一个或多个catch子句。catch子句包含一个关键字catch,一个括号括起来的对象声明,一个语句块。当选中某个catch子句处理完毕后,程序就跳到所有catch子句之后的地方继续执行。如果没有catch子句,程序一般会非正常退出。

另外,try语句块具有块作用域,出了该块,不能在外部访问块内引入的变量。

 

 

 

练习5.1:什么是空语句?什么时候会用到空语句?

空语句就是只含有一个单独分号的语句。当语法上需要一条语句,逻辑上不需要时,就需要使用空语句

 

练习5.2:什么是块?什么时候会用到块?

块就是用花括号括起来的语句或者声明序列,当语法上需要一条语句,但是逻辑上需要多条语句,此时就要使用块。


练习5.3:使用逗号运算符(参见4.10节,第104页)重写1.4.1节(第10页)的 while 循环,使它不再需要块,观察改写之后的代码的可读性提高了还是降低了。

#include <iostream>

using namespace std;

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

    return 0;
}

改写后的可读性降低。

 

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

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

(b) while (bool status = find(word))
{
    /* . . . */
}
if (!status)
{
    /* . . . */
}

(a) 用循环遍历string s,语法错误,while头部只能是一个表达式或者初始化了的变量声明。应该将iter放在while外面定义。

(b) while头部定义的变量只在当前循环内可见,if无法访问,应该将status的定义放在最外面。

 

练习5.5:写一段自己的程序,使用if else 语句实现把数字成绩转换为字母成绩的要求。

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    const vector<string> scores = {"F", "D", "C", "B", "A", "A++"};
    string lettergrade;
    int grade;
    while (cin >> grade)
    {
        if (grade < 60)
            lettergrade = scores[0];
        else
            lettergrade = scores[(grade - 50) / 10];
        cout << lettergrade << '\n';
    }
    return 0;
}

 

练习5.6:改写上一题的程序,使用条件运算符(参见4.7节,第134页)代替if else语句。

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    const vector<string> scores = {"F", "D", "C", "B", "A", "A++"};
    string lettergrade;
    int grade;
    while (cin >> grade)
    {
        (grade < 60) ? ( lettergrade = scores[0]) :    (lettergrade = scores[(grade - 50) / 10]);
        cout << lettergrade << '\n';
    }
    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) ival1 = ival2 后面缺少分号。

(b) 应该用花括号括起来。

(c) if (!ival) 应该改为else。

(d) if (ival = 0) 应该改为 if (ival == 0)。


练习5.8:什么是“悬垂else”?C++语言是如何处理else子句的?

悬垂else就是一个if语句嵌套在另一个if语句内部,使得if分支多于else分支。C++规定else与离它最近的尚未匹配的if匹配。

 

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

#include <iostream>

using namespace std;

int main()
{
    char c;
    size_t num = 0;
    while (cin >> c)
    {
        if (c == 'a')
            ++num;
        if (c == 'e')
            ++num;
        if (c == 'i')
            ++num;
        if (c == 'o')
            ++num;
        if (c == 'u')
            ++num;
    }
    cout << num << endl;
    return 0;
}

 

练习5.10:我们之前实现的统计元音字母的程序存在一个问题:如果元音字母以大写形式出现,不会被统计在内。编写一段程序,既统计元音字母的小写形式,也统计元音字母的大写形式,也就是说,新程序遇到'a'和'A'都应该递增 aCnt 的值,以此类推。

#include <iostream>

using namespace std;

int main()
{
    char c;
    size_t num = 0;
    while (cin >> c)
    {
        if (c == 'a' || c == 'A')
            ++num;
        if (c == 'e' || c == 'E')
            ++num;
        if (c == 'i' || c == 'I')
            ++num;
        if (c == 'o' || c == 'O')
            ++num;
        if (c == 'u' || c == 'U')
            ++num;
    }
    cout << num << endl;
    return 0;
}

 

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

#include <iostream>

using namespace std;

int main()
{
    size_t num;
    char ch;
    while (cin >> ch)
    {
        switch (ch)
        {
        case 'a':      case 'A':      case 'e':      case 'E':      case 'i':      case 'I':      case 'o':
        case 'O':      case 'u':      case 'U':      case ' ':      case '\t':     case '\n':
            ++num;
        }
    }
    return 0;
}

 

练习5.12:修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量: ff、fl和fi。

#include <iostream>

using namespace std;

int main()
{
    size_t num;
    string s;
    while (cin >> s)
    {
        if (s == "ff" || s == "fl" || s == "fi")
        {
            ++num;
        }
    }
    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) 每个case缺少break语句。

case 'a': aCnt++;
break;
case 'e': eCnt++;
break;
default: iouCnt++;
break;

(b) default标签中的ix 未定义。修改为

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


(c) case标签错误,变量名错误。修改为

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 10:
evencnt++;
break;
}


(d) case 标签必须是整型常量表达式。修改为

constexpr 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次。

#include <iostream>

using namespace std;

int main()
{
    string s1, s2;
    if (cin >> s1)
    {
        int cnt = 1;
        while (cin >> s2)
        {
            if (s1 == s2)
                ++cnt;
            else
            {  
                cout << s1 << "occurs " << cnt << " times" << endl;
                s1 = s2;
                cnt = 1;
            }
        }
        cout << s1 << "occurs " << cnt << " times" << endl;
    }

    return 0;
}

 

练习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) if不可以访问for的局部变量ix,应该将ix放在for的前面定义

(b) for的语法格式错误,应改为for( ; ix != sz; ++ix)

(c) 死循环,ix != sz 恒成立,不应该使用 ++ix,++sz

 

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

#include <iostream>

using namespace std;

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

    for (int i = 0; i != 100; ++i)
    {
        sum += i;
    }
    return 0;
}

如果只能使用一种,我倾向于for循环,因为for循环头部提供更多的选项,可以更多的控制循环,相对while循环来说更强大易用。

 

练习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> v1{0, 1, 1, 2}, v2{0, 1, 1, 2, 3, 5, 8};
    if (v1.size() < v2.size())
    {
        for (size_t i = 0; i != v1.size(); ++i)
        {
            if (v1[i] != v2[i])
                cout << "not equal\n";
        }
    }
    else
    {
        for (size_t i = 0; i != v2.size(); ++i)
        {
            if (v1[i] != v2[i])
                cout << "not equal\n";
        }
    }
    return 0;
}

 

练习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 (ival = get_response());
(c) do {
        ival = get_response();
    } while (ival);

(a) 语法错误,do后面,while前面的语句应该用花括号括起来

(b) 语法错误,do while条件使用的表达式规定必须是在循环前定义!

(c) 语法错误,do while条件使用的表达式规定必须是在循环前定义!


练习5.19:编写一段程序,使用do while 循环重复地执行下述任务:首先提示用户输入两个string对象,然后挑出较短的那个并输出它。

#include <iostream>

using namespace std;

int main()
{
    string s1, s2;
    char c;
    do
    {
        cout << "input two strings:";
        cin >> s1 >> s2;
        if (s1.size() > s2.size())
            cout << s1 << endl;
        else
            cout << s2 << '\n' << "More ? Enter 'y' or 'n' " << endl;
        cin >> c;
    }
    while (c != 'n');
    return 0;
}

 


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

#include <iostream>

using namespace std;

int main()
{
    string s1, s2;
    if (cin >> s1)
    {
        while (cin >> s2)
        {
            if (s1 == s2)
            {
                cout << s2;
                break;
            }
            else
            {
                s1 = s2;
            }
        }
    }

    return 0;
}

 

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

#include <iostream>

using namespace std;

int main()
{
    string s1, s2;
    if (cin >> s1)
    {
        while (cin >> s2)
        {
            if (s1 == s2 && isupper(s2[0]))
            {
                cout << s2 << endl;
                break;
            }
            else
            {
                s1 = s2;
            }
        }
    }

    return 0;
}

 

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

#include <iostream>

using namespace std;

int main()
{
    int sz = get_size();
    while (sz <= 0)
        sz = get_size();
    return 0;
}

 

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

#include <iostream>

using namespace std;

int main()
{
    int i1, i2;
    cin >> i1 >> i2;
    if (i2 == 0)
        throw "ERROR! Division by zero!";

    cout << i1 / i2 << endl;
    return 0;
}

 

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

程序终止。


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

#include <iostream>

using namespace std;

int main()
{
    int i1, i2;
    while (cin >> i1 >> i2)
    {    try
        {
            if (i2 == 0)
                throw string("ERROR! Division by zero!");
        }
        catch (string s)
        {
            cout << s << endl;
            cout << "Try again? 'Y' or 'N'";
            char c;
            cin >> c;
            if (!cin || c == 'n')
                break;
            else
                continue;
        }
        cout << i1 / i2 << endl;
    }
    return 0;
}

 

posted @ 2016-01-06 19:39  impluse  阅读(714)  评论(0编辑  收藏  举报