【C++学习】第五章 控制结构 5.14-5.20节

网友投稿 2019-04-02 11:54

《信息学奥赛C++培训教程》共计八章,主要用于编程语言C++的基础学习,适用于C++初学者。

第5章 C++的字符串流5.14  for结构使用举例

下面的例子显示for结构中改变控制变量的方法。在每个例子中,我们都编写相应的for结构首部。注意循环中递减控制变量的关系运算符的改变。

a)将控制变量从1变到100,增量为1。

      for(int i=l;i<=100;i++)

b)将控制变量从100变到1,增量为-1。

      for(int i=100;i>=1; i--)

c)控制变量的变化范围为7到77。

      for(int i= 7;i <= 77; i+= 7)

d)控制变量的变化范围为20到2。

      for(int i=20;i>=2;i-=2)

c)按所示数列改变控制变量值:2、6、8、11、14、17、20。

      for(int j=2;j<=20;j+=3)

f)按所示数列改变控制变量值:99、88、77、66、55、44、33、22、ll、0。

     for(int j=99; j>=0;  j-=11)

下面两个例子提供for结构的简单应用。图5.20所示的程序用for结构求2到100的所有整数的总和。

注意图5.20中for结构体可以用下列逗号运算符合并成for首部的右边部分:

for(int number = 2;                  // initialization

       number <= 100;                   // continuation condition

       sum += number, number += 2)         // total and increment

初始化sum=0也可以合并到for的初始化部分。

1 // Fig. 5.20:fig05_20.cpp

2 // Summationwith for

3 #include

4

5 int main()

6 {

7    int sum = 0;

8

9    for (int number = 2; number <= 100;number += 2 )

10         sum += number;

11

12   cout << "Sum is "<<sum <<="" endl;<="" p="">

13

14   return 0;

15 }

输出结果:

    sum is 2550

图5.20  用for语句求和

下列用for结构计算复利。考虑下列问题:

一个人在银行存款1000.00美元,每利率为5%。假设所有利息留在账号中,则计算10年间每年年末的金额并打印出来。用下列公式求出金额:

    a=P(1+r)n

其中:

    P是原存款(本金)

    r是年利率

    n是年数

    a是年未本息

    这个问题要用一个循环对10年的存款进行计算。解答如图5.21

for结构执行循环体10次,将控制变量从1变到10,增量为1。C++中没有指数运算符,因此要用标准库函数pow。函数pow(x,y)计算x的y次方值。函数pow取两个类型为double的参数并返回double值。类型double与float相似,但double类型的变量能存放比float精度更大的数值。C++把常量(如图5.21中的1000.0和.05)当作double类型处理。

1 // Fig. 5.21:fig02_21.cpp

2 //Calculating compound interest

3 #include

4 #include

5 #include

6

7 int main()

8 {

9    double amount,           // amount on deposit

10         principal = 1000.0,  // starting principal

11         rate = .05;        // interest rate

12

13   cout << "Year" <<setw( 21="" )<="" p="">

14      << "Amount on deposit"<< endl;

15

16   for ( int year = 1; year <= 10; year++ )

17  {

18     amount = principal * pow{ 1.0 + rate, year);

19     cout << setw( 4 ) << year

20         << setiosflags( ios::fixed Iios::showpoint )

21         << setw( 21 ) <<setprecision( 2="" )<="" p="">

22         << amount << endl;

23    }

24

25

26 }

输出结果:

Year     Amount on deposic

1                  1050.00

2                  1102.50

3                  1157.62

4                  1215.51

5                  1276.28

6                  1340.10

7                  1407.10

8                  1477.46

9                  1551.33

10                 1628.89

图 5.21 用for结构计算复利

这个程序必须包括math.h才能编译。函数pow要求两个double参数,注意year是个整数。math.h文件中的信息告诉编译器将year值转换为临时double类型之后再调用函数。这些信息放在pow的函数原型(function prototype)中。第3章将介绍函数原型并总结pow函数和其他数学库函数。

程序中将变量amount、principal和rate声明为double类型,这是为了简单起见,因为我们要涉及存款数额的小数部分,要采用数值中允许小数的类型。但是,这可能造成麻烦,下面简单介绍用float和double表示数值时可能出现的问题(假设打印时用setprecision(2)):机器中存放的两个float类型的值可能是14.234(打印14.23)和18.673(打印18.67)。这两个值相加时,内部和为32.907,打印32.91。结果输出如下:

   14.23

 + 18.67

-----------

   32.91

但这个加式的和应为32.90。

输出语句:

    cout<<setw(4)<<year< p="">

    <<setiosflags(10s::fixed  los::showpoint)<="" p="">

    << setw(21) << setpreclsion(2)

    << amount<<endl;< p="">

用参数化流操纵算于setw、setiosflags和setprecision指定的格式打印变量year和amount的值。调用selw(4)指定下一个值的输出域宽(field width)为4,即至少用4个字符位置打印这个值。如果输出的值宽度少于4个字符位,则该值默认在输出域中右对齐(ishljustl^ed),如果输出的值宽度大于4个字符,则该值将输出域宽扩大到能放下这个值为止。调用setiosflag(ios::left)可以指定输出的值为左对齐(1eft justified)。

上述输出中的其余格式表示变量amount打印成带小数点的定点值(用setiosflags(ios::fixed |ios::showpoint)指定),在输出域的21个字符位中右对齐(用setw(21)指定),小数点后面为两位(用setprecision(2)指定)。

注意计算1.0+rate作为pow函数的参数,包含在for语句体中。事实上,这个计算产生的结果在每次循环时相同,因此是重复计算(是一种浪费)。

5.15  switch多项选择结构

前面介绍了if单项选择结构和if/else双项选择结构。有时算法中包含一系列判断,用一个变量或表达式测试每个可能的常量值,并相应采取不同操作。C++提供的switch多项选择结构可以进行这种判断。

switch结构包括一系列case标记和一个可选default情况。图5.22中的程序用switch计算学生考试的每一级人数。

1 // Fig.5.22:fig02 22.cpp

2 // Countingletter grades

3 #include

4

5 int main()

6 {

7    int grade,      // one grade

8       aCount = 0,  // number of A's

9       bCount = 0,  // number of B's

10      cCount = O,  // number of C's

11      dCount = 0,  // number of D's

12      fCount = 0;  // number of F's

13

14   cout << "Enter the lettergrades." << endl

15       << "Enter the EOF characterto end input." << endl;

16

17   while ( ( grade = cin.get() ) != EOF ) {

18

19     switch ( grade ) {     // switch nested in while

20

21       case 'A':  // grade was uppercase a

22       case 'a':  // or lowercase a

23         ++aCount;

24         break; // necessary to exit switch

25

26       case 'B':  // grade was uppercase B

27       case 'b':  // or lowercase b

28          ++bCount;

29         break;

30

31       case 'C':  // grade was uppercase C

32       case 'c':  // or lowercase c

33          ++cCount;

34         break;

35

36       case 'D':  // grade was uppercase D

37       case 'd':  // or lowercase d

38          ++dCount;

39         break;

40

41       case 'F':  // grade was uppercase F

42       case 'f':  // or lowercase f

43          ++fCount;

44         break;

45

46       case 'n': // ignore newlines,

47       case 't': // tabs,

48       case ' ':  // and spaces in input

49         break;

50

51       default: // catch all other characters

52         cout << "Incorrect lettergrade entered."

53

54              << "Enter a newgrade." << endl;

55         break; // optional

56      }

57    }

58    cout << "nnTotals for eachletter grade are:"

59              "nA: "<< aCount

60                 << "nB: "<< bCount

61                 << "nC:"<< cCount

62                << "nD:"<< dCount

63                << "nF:"<< fCount << endl;

64

65   return 0;

66 }

输出结果:

Enter theletter grades.

Enter the EOFcharacter to end input.

A

B

C

C

A

D

F

C

E

Incorrectletter grade entered. Enter a new grade.

D

A

B

Totals for eachletter grade are:

A: 3

B: 2

C: 3

D: 2

F: 1

图 5.22 使用switch举例

程序中,用户输入一个班代表成绩的字母。在while首部中:

    while( (grade=cin.get()) !=EOF)

首先执行参数化赋值(grade=cin.get())。cin.get()函数从键盘读取一个字符,并将这个字符存放在整型变量grade中。字符通常存放在char类型的变量中,但是,C++的一个重要特性是可以用任何整数数据类型存放字符,因为它们在计算机中表示为1个字节的整数。这样,我们根据使用情况,可以把把字符当作整数或字符。例如,下列语句:

    cout << "The  character(" <<'a' <<")  has  the value  "

    << static_cast('a') << endl;

打印字符a及其整数值如下所示:   

    The character  (a) has the value 97

整数97是计算机中该字符的数字表示。如今许多计算机都使用ASCII(AmericanStandardCodefor

lnformationInterehange,美国标准信息交换码)字符集(character set),其中97表示小写字母“a”。附录中列出了ASCII字符及其十进制值的列表。

赋值语句的整个值为等号左边变量指定的值。这样,赋值语句grade=cin.get()的值等于cin.get()返回的值,赋给变量grade。赋值语句可以用于同时初始化多个变量。例如:

    a = b = c = 0;

首先进行赋值c=o(因为:运算符从右向左结合),然后将c=0的值赋给变量b(为0),最后将b=(c=0)的值赋给变量a(也是0)。程序中,赋值语句grade=cin.get()的值与EOF值(表示文件结束的符号)比较。我们用EOF(通常取值为-1)作为标记值。用户输入一个系统识别的组合键,表示文件结束,没有更多要输入的数据。EOF是头文件中定义的符号化整型常量。如果grade的取值为EOF,则程序终止。我们选择将这个程序中的字符表示为int,因为EOF取整数值(通常取值为-1)。

用户通过键盘输入成绩。按Enter(或Return)键时,cin.get()函数一次读取一个字符。如果输入的字符不是文件结束符,则进入switch结构。关键字switch后面是括号中的变量名grade,称为控制表达式(controlling expression)。控制表达式的值与每个case标记比较。假设用户输入成绩c,则c自动与switch中的每个case比较。如果找到匹配的(case'C:'),则执行该case语句。对于字母C的case语句中.cCount加1,并用break语句立即退出switch结构。注意,与其他控制结构不同的是,有多个语句的case不必放在花括号中。

break语句使程序控制转到switch结构后面的第一条语句。break语句使switch结构中的其他case不会一起运行。如果switch结构中不用break语句,则每次结构中发现匹配时,执行所有余下case中的语句(这个特性在几个case要完成相同操作时有用,见图5.22的程序)。如果找不到匹配,则执行default case并打印一个错误消息。

每个case有一个或几个操作。switch结构与其他结构不同,多个语句的case不必放在花括号中。图5.23显示了一般的swish多项选择结构(每个case用一个break语句)的流程图。

从流程图中可以看出,case末尾的每个break语句使控制立即退出switch结构。注意,流程图(除了小圆框和流程之外)也只能包含矩形框和菱形框。这是我们强调的操作/判断编程模型。程序员的任务就是根据算法需要用堆栈和嵌套两种方法组合其他几种控制结构,然后填入算法所要的操作和判断,从而生成程序。嵌套控制结构很常见,但程序中很少出现嵌套swilch结构。

    在图5.22中的switch结构中,第46到第49行:

    case 'n':

    case 't':

    case ' ' :

    break;

使程序跳过换行符、制表符和空白字符。一次读取一个字符可能造成一些问题。要让程序读取字符,就要按键盘上的Enter键将字符发送到计算机中,在要处理的字符后面输入换行符。这个换行符通常需要特殊处理,才能使程序正确工作。通过在switch结构中包含case语句,可以防止在每次输入中遇到换行符、制表符或空格时defaultcase打印错误消息。

注意几个标号列在一起时(如图5.22中的case'D':case 'd':)表示每case发生一组相同的操作。

使用switch结构时,记住它只用于测试常量整型表达式(constant integral expression),即求值为一个常量整数值的字符常量和整型常量的组合。字符常量表示为单引号中的特定字符(如,'A'),而整型常量表示为整数值。

本教程介绍面向对象编程时,会介绍实现switch逻辑的更精彩方法。我们使用多态方法生成比使用switch逻辑的程序更清晰、更简洁、更易维护和扩展的程序。

C++之类可移植语言应有更灵活的数据类型长度,不同应用可能需要不同长度的整数。C++提供了几种表示整数的数据类型。每种类型的整数值范围取决于特定的计算机硬件。除了类型int和char外,C++还提供short(short int的缩写)和long(long int的缩写)类型。short整数的取值范围是±32767。

对于大多数整数计算,使用long类型的整数已经足够。long整数的取值范围是±2147483647。在大多数系统中,int等价于short或long。int的取值范围在short和long的取值范围之间。char数据类型可以表示计算机字符集中的任何字符,char数据类型也可以表示小整数。

5.16  do/while重复结构

do/while重复结构与while结构相似。在while结构中,先在循环开头测试循环条件之后再执行循环体。do/while重复结构执行循环体之后再测试循环条件,因此,do/while结构至少执行循环体一次。do/while结构终止时,继续执行while语句后面的话句。注意,如果结构体中只有一条浯句,则不必在do/while结构中使用花括号。但通常还是加上花括号,避免棍淆while与do/while重复结构。

例如:

    while (condition)

通常当作while结构的首部。结构体中只有一条语句的do/while结构中不使用花括号时:

    do

          statement

    while ( condition );

最后一行while(condition)可能被误解成while结构包含空语句。这样,只有一个语句的do/while结构通常写成如下形式:

    do {

          statement

    )while ( condltion);

图5.24所示的程序用do/while重复结构打印数字1到10。注意控制变量counter在循环条件测试中是前置自增的。另外,只有一个语句的do/while结构也使用了花括号。

1 // Fig. 5.24:fig0224.cpp

2 // Using thedo/while repetition structure

3 #include

4

5 int main()

6 {

7     int counter = l;

8      

9   do {

10     cout << counter <<  " " ;

11  } while ( ++counter <= 10 );

12

13  cout << endl;

14

15  return 0;

16  }

 输出结果:

    1   2    3    4   5    6  7   8    9    10

图5.24 使用 do/while 重复结构

do/while重复结构如图5.25。这个流程图显示循环条件要在至少进行一次操作之后才执行。注意.流程图(除了小圆框和流程之外)也只能包含矩形框和菱形框,这是我们强调的操作/判断编程模型。程序员的任务就是根据算法需要用堆栈和嵌套两种方法组合其他几种控制结构,然后填入算法所要的操作和判断,从而生成程序。

5.17  break和continue语句

break和continue语句改变控制流程。break语句在while、for、do/while或switch结构中执行时,使得程序立即退出这些结构,从而执行该结构后面的第一条语句。break语句常用于提前从循环退出或跳过switch结构的其余部分(如图5.22)。图5.26演示了for重复结构中的break语句,if结构发现x变为5时执行break,从而终止for语句,程序继续执行for结构后面的cout语句。循环只执行四次。

注意这个程序中控制变量x在for结构首部之外定义。这是因为我们要在循环体中和循环执行完毕之后使用这个控制变量。

continue语句在while、for或do/while结构中执行时跳过该结构体的其余语句,进入下一轮循环。在while和do/while结构中,循环条件测试在执行continue语句之后立即求值。在for结构中,执行递增表达式,然后进行循环条件测试。前面曾介绍过.while结构可以在大多数情况下取代for结构。但如果while结构中的递增表达式在continue语句之后,则会出现例外。这时,在测试循环条件之前没有执行递增,并且while与for的执行方式是不同的。图5.27在for结构中用continue语句跳过该结构的输出语句,进入下一轮循环。

1 // Fig.5.26:fig02.26.cpp

2 // Usinq thebreak statement in a for structure

3 #include<~ostream.h>

4

5 int  main()

6 {

7     // x declared here so it can be used afterthe loop

8     int x;

9

10    for ( x = 1; x <= 10; x++ ) {

11

12    if { x == 5 )

13        break;  // break loop only if x is 5

14

15    cout << x <<" ";

16    }

17

18  cout << "nBroke out of loop at xof" << x << endl;

19  return 0;

20 }

输出结果:

  l 2  3  4

  Broke out of loop at x of 5

图5.26  for重复结构中的break语句

1  // Fig. 5.27: fig02_27.cpp

2  // Using the continue statement in a forstructure

3 #include

4

5 int main()

6 {

7    for ( iht x = 1; x <- 10; x++ } {

8

9       if {x==5)

10         continue;  // skip remaining code in loop

11                  // only if x is 5

12

13      cout << x <<" ";

14  }

15

16      cout << "nUsed continue toskip printing the value 5"

17             << endl;

18      return 0;

19 }

l  2 3  4  5 6  7  9 9  10

    USed continue to skip printing the value 5

图 5.27  在for结构用continue语句

5.18  逻辑运算符

前面只介绍了courter<=10、total>1000和number!=sentinel Value之类的简单条件(simplecondition)。我们用关系运算符>、<、>=、<=和相等运算符==、!=表示这些条件。每个判断只测试一个条件。要在每个判断中测试多个条件,可以在不同语句中或嵌套if(if/else)结构中进行这些测试。

C++提供的逻辑运算符(logical operator)可以用简单条件组合成复杂条件。逻辑运算符有逻辑与(&&)、逻辑或(||)和逻辑非(!),下面将举例说明。

假设要保证两个条件均为true之后再选择某个执行路径,这时可以用&&逻辑运算符:

    if { gender == 1 && age>= 65 )

        ++senior Females;

这个if语句包含两个简单条件。条件gender==1确定这个人是男是女,条件age>=65确定这个人是否为老年人。&&逻辑运算符左边的简单条件先求值,因为::的优先级高于&&。如果需要,再对&&逻辑运算符右边的简单条件求值,因为>=的优先级高级于&&(稍后将会介绍,&&逻辑运算符右边的条件只在左边为true时才求值)。然后if语句考虑下列组合条件:

    gender==l&&age>=65

如果两边的简单条件均为true,则这个条件为true。最后.如果这个条件为true,则seniorFemales递增1。如果两边的简单条件有一个为false,则程序跳过该递增,处理if后面的语句。上述组合条件可以通过增加多余的括号而变得更加清楚:

    (gender==1)&&(age>=65)

图5.28的表格总结了&&逻辑运算符。表中显示了表达式1和表达式2的四种false和true值组合,这种表称为真值表(truth table)。C++对所有包括逻辑运算符、相等运算符和关系运算符的所有表达式求值为false或true。

表达式1                        表达式2                表达式1&&表达式2

    false                          false                    false

    false                          true                     false

    true                           false                    false

    true                           true                     true

图 5.28 逻辑与(&&)运算符真值表

下面考虑逻辑或运算符(||)。假设要保证两个条件至少有一个为true之后再选择某个执行路径,这时可以用逻辑或运算符,如下列程序段所示:

  if(semesterAverage)>=90||finalExam>=90)

    cout<<"Student grade isA"<<endl;< p="">

上述条件也包含两个简单条件。条件semesterAverage>=90确定学生整个学期的表现是否为"A",条件finalExam>=90确定该生期末考试成绩是否优秀。然后if语句考虑下列组合条件:

    semesterAverage>=90 || finalExam>=90

如果两个简单条件至少有一个为true,则该生评为“A”。注意只有在两个简单条件均为false时才不打印消息"Student grade isA"。图5.29是逻辑或(||)运算符的真值表。

&&逻辑运算符的优先级高于||运算符。这两个运算符都是从左向右结合。包含&&和||运算符的表达式只在知道真假值时才求值。这样,求值下列表达式:

    gender==1 && age>=65

将在gender不等于1时立即停止(整个表达式为false),而gender等于1时则继续求值(整个表达式在age>=65为true时为true。

表达式1                    表达式2                    表达式1&&表达式2

    false                      false                      false

    false                      true                       true

    true                       false                      true

    true                       true                       true

图 5.29 逻辑或(||)运算符真值表

C++提供了逻辑非(!)运算符,使程序员可以逆转条件的意义。与&&和||运算符不同的是,&&和||运算符组合两个条件(是二元运算符),而逻辑非(!)运算符只有一个操作数(是一元运算符)。逻辑非(!)运算符放在条件之前,在原条件(不带逻辑非运算符的条件)为false时选择执行路径,例如:

    if(grade!=sentinelValue)

        cout<<"The next gradeis"<<grade<<endl;< p="">

这种灵活性有助于程序员以更自然或更方便的方式表达条件。

图5.31显示了前面介绍的c++运算符的优先级和结合律。运算符优先级从上到下逐渐降低。

    运算符                                        结合律            类型

    ()                                            从左向右           括号

 ++ --  +  - !  static_cast()                从右向左           一元

    * /  %                                       从左向右           乘

    + -                                          从左向右           加

    << >>                                   从左向右           插入/读取

    < <=  >  >=                                从左向右           关系

    == !=                                      从左向右           相等

    &&                                       从左向右           逻辑与

    ||                                         从左向右           逻辑或

    ?:                                        从右向左           条件

    = +=  -=  *= /=  %=                     从右向左            赋值

    ,                                          从左向右            逗号

图 5.31 运算符优先级和结合律

5.19  混淆相等(==)与赋值(=)运算符

这是C++程序员常见的错误,包括熟练的C++程序员也会把相等(==)与赋值(=)运算符相混淆。这种错误的破坏性在于它们通常不会导致语法错误,而是能够顺利编译,程序运行完后,因为运行时的逻辑错误而得到错误结果。

C++有两个方面会导致这些问题。一个是任何产生数值的表达式都可以用于任何控制结构的判断部分。如果数值为0,则当作false,如果数值为非0,则当作true。第二是C++赋值会产生一个值,即赋值运算符左边变量所取得的值。例如,假设把下列代码:

    if(payCode==4)

       cout <<"You get a bonus!"<< endl;

误写成如下形式:

    if(payCode=4)

       cout <<  "You get a bonus!" << endl;

第一个if语句对paycode等于4的人发奖金。而第二个if语句则求值if条件中的赋值表达式为常量4。由于非0值解释为true,因此这个if语句的条件总是true,则人人都获得一份奖金,不管其paycode为多少。更糟的是,本来只要检查paycode,却已经修改了Poycode。

变量名可称为左值(lvalue),因为它可以放在赋值运算符左边。常量称为右值(rvalue),因为它只能放在赋值运算符右边。注意,左值可以作为右值,但右值不能用作左值。

还有一个问题也同样麻烦。假设程序员要用下列简单涪句给一个变量赋值:

    x = 1;

但却写成:

    x == 1;

这也不是语法错误,编译器只是求值条件表达式。如果x等于1,则条件为true,表达式返回true值。

如果x不等于1,则条件为false,表达式返回false值。不管返回什么值都没有赋值运算符,因此这个值丢失,x值保持不变,有可能造成执行时的逻辑错误。但这个问题没有简单的解决办法。

5.20  结构化编程小结

 结构化编程提倡简单性。Bohm和Jacopini已经证明,只需要三种控制形式:

    ●顺序(sequence)

    ●选择(selection)

    ●重复(repetmon)

顺序结构很简单,选择可以用三种方法实现:

    ●if结构(单项选择)

    ●if/else结构(双项选择)

    ●switch结构(多项选择)

事实上很容易证明简单的if结构即可提供任何形式的选择,任何能用if/else结构和switch结构完成的工作,也可以组合简单if结构来实现(但程序可能不够流畅)。

    重复可以用三种方法实现:

    ●while结构

    ●do/while结构

    ●for结构

很容易证明简单的while结构即可提供任何形式的重复,任何能用do/while和for结构完成的工作,也可以组合简单while结构来实现(但程序可能不够流畅)。

    根据以上结果,C++程序中所需的任何控制形式均可以用下列形式表示:

    ●顺序

    ●if结构(选择)

    ●while结构(重复)

这些控制结构只要用两种方式组合,即嵌套和堆栈。事实上,结构化编程提倡简单性。

--end--

声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com