第2章 程序控制结构

语句是程序的基本语法成分。程序设计语言的语句按功能可以分成三类:说明语句、操作语句和控制语句。

说明语句不是执行语句。说明语句有两个作用:一是用于定义,例如,变量说明语句用于定义变量名并指示编译器分配内存,类型说明语句用于定义数据的组织方式;二是用于声明程序连接信息,如函数原型、静态量、全局量说明语句等。因此,为了区分说明语句的不同性质,有时特别把功能不同的说明语句分别称为定义语句和声明语句。

操作语句用于描述对数据的处理。例如,表达式语句和输入/输出语句分别表示对数据的运算和传输。

控制语句用于控制程序的执行流程。所有程序都只能包含3种控制结构:顺序结构、选择结构和循环结构。顺序结构是系统预置的,即除非特别指定,计算机总是按指令编写的顺序一条一条地执行。选择结构和循环结构由特定语句组织。

本章讨论C++的选择结构语句和循环结构语句及其应用。

2.1 选择控制

实际编写程序时,有时需要对给定的条件进行判断,并根据判断的结果选择不同的操作。

例如,给定三条边的长度,判断能否构成三角形。若能构成三角形,则求其面积;否则,显示“不能构成三角形”的信息。

又如,求一元二次方程ax2+bx+c=0的根,要对b2-4ac作判断。当b2-4ac的值等于0时,求重根;大于0时,求两个实根;小于0时,求共轭复根。

构成选择结构的语句称为条件语句。C++使用if语句和switch语句构成选择结构。

2.1.1 if语句

1.if语句的形式和执行流程

if语句有两种形式:一个分支的if语句和两个分支的if_else语句。

(1)一个分支的if语句

语句形式为:

            if(表达式)语句;

其中,“表达式”一般为逻辑表达式,表示执行条件。若为其他类型表达式,则C++也把其结果作为逻辑值处理。“语句”可以是一个简单语句,也可以是复合语句或其他结构语句。

if语句首先计算“表达式”的值,如果值为true(非0),则执行“语句”;否则,即“表达式”的值为 false(0),视“语句”为空,转向执行后续语句。执行流程如图2.1所示。例如:

图2.1 if语句的执行流程

            if(x>0)  cout<<x<<endl;

首先判断x的值是否大于0,若x的值大于0,则输出x的值并换行;否则不输出x的值。又如:

            if ((a+b > c) && (b+c > a) && (c+a > b))
            {  s=(a+b+c)/2.0;
              area = sqrt(s * (s-a) * (s-b) * (s-c));
              cout << "area = "<< area << endl;
            }

先计算逻辑表达式(a+b>c)&&(b+c>a)&&(c+a>b)的值,若为true,则按顺序执行花括号相括的复合语句;否则跳过该语句。

(2)if_else语句

语句形式为:

            if(表达式)语句1
              else 语句2

其中,“表达式”一般为关系表达式或逻辑表达式,表示执行条件。“语句1”和“语句2”为简单语句、复合语句或其他结构语句。

if_else语句的执行情况如图2.2所示。例如:

图2.2 if_else语句的执行流程

            if (x>y) cout << "max = "<< x << endl;
            else  cout<<"max="<<y<<endl;

判断x是否大于y,若是,则输出x的值;否则,输出y的值。又如:

            if ((a+b > c) && (b+c > a) && (c+a > b))
              {  s=(a+b+c)/2.0;
                  area = sqrt(s * (s-a) * (s-b) * (s-c));
                  cout << "area = "<< area << endl;
              }
            else  cout<<"It is not a trilateral."<<endl;

先计算逻辑表达式(a+b>c)&&(b+c>a)&&(c+a>b)的值,若为真,则按顺序执行花括号内的三个语句;否则,输出字符串“It is not a trilateral.”。

2.if语句的嵌套

if语句中的执行语句如果是另一个if语句,则称为嵌套if语句。例如:

            if(x>0)  y=x;
            else   if(x<0)  y=-x;
                  else  y=0;

当x<=0时,执行else分支,嵌套了另一个if语句,分别处理x<0和x==0的情况。又如:

            if(score>=90)  cout<<"Grade A:";
            else   if(score>=80)  cout<<"Grade B:";
                  else   if(score>=70)  cout<<"Grade C:";
                      else  if(score>=60)  cout<<"Grade D:";
                            else  cout<<"Grade E:";

也是if语句的嵌套。

if语句有不同的嵌套形式,但要注意 if 与 else 的配对关系。C++语言规定,else 总是与它接近的if 配对。上述两个语句可以添加括号表示等价的嵌套匹配关系,改写为:

            if(x>0)  {y=x;}
            else  {  if(x<0)  y=-x;
                    else  y=0;
                }

            if(score>=90)  {cout<<"Grade A:";}
            else   {  if(score>=80){cout<<"Grade B:";}
                    else  {  if(score>=70){cout<<"Grade C:";}
                            else  {  if(score>=60){cout<<"Grade D:";}
                                  else  {cout<<"Grade E:";}
                                }
                        }
                  }

使用复合语句,可以改变条件语句的执行流程。例如,以下形式的语句:

            if(表达式 0)         等价于:         if(表达式 0)
                if(表达式 1)                         {  if(表达式 1)
                    语句 1;                               语句 1;
                else  if(表达式 2)                      else
                          语句 2;                       { if(表达式 2)
                      else                                     语句 2;
                          语句 3;                          else
                                                                语句 3;
                                                          }
                                                    }

“语句 1”的执行条件是“表达式 0 && 表达式 1”为true;

“语句 2”的执行条件是“表达式 0 &&!表达式 1 &&表达式 2”为true;

“语句 3”的执行条件是“表达式 0 &&!表达式 1 &&!表达式 2”为true。

使用花括号构造复合语句,可以改变条件语句的配对关系。例如,上述语句改写为:

            if (表达式 0)
              {  if(表达式 1)
                      语句 1;
                  else
                      if (表达式 2)
                          语句 2;
              }
            else
              语句 3;

这时,“语句 3”的执行条件为“!表达式 0”,即“表达式 0”的值为false时执行“语句 3”。

3.应用举例

【例2-1】从键盘上输入3个整数,求这3个数中的最大值。

假设输入的3个数分别存储在变量a、b和c中,变量max存储最大值。求最大值的算法可以为:先比较a和b的值并把较大值赋给max;然后将c与max进行比较,若c>max,则把c赋给max。此时,max的值就是3个数中的最大值了。程序如下:

            #include <iostream>
            using namespace std;
            int main()
            {  int a,b,c,max;
              cout << "a, b, c = ";
              cin >> a >> b >> c;
              if(a>b)  max=a;            //求a,b之中的较大值,赋给max
                  else  max=b;
              if(c>max)  max=c;          //求max,c之中的较大值,赋给max
              cout << "max = " << max << endl;
            }

简单的条件语句,通常可以用条件运算代替。程序中的if语句:

            if(a>b)   max=a;
              else   max=b;

可以写为: max = a>b ? a :b;

【例2-2】输入3条边的边长,若这3条边构成三角形就求该三角形的面积;否则,输出“不是三角形”的信息。

假设三角形的3条边的边长为a、b和c,则这3条边构成三角形的条件是:任意两条边之和都大于第三边。如果这3条边能构成三角形,则求三角形的面积公式为:

式中,s = (a + b + c) / 2。

按求三角形的面积公式,编写程序如下:

            #include <iostream>
            #include<cmath>                      //包含声明数学函数的头文件
            using namespace std;
            int main()
            {  double a,b,c,s,area;
              cout << "a, b, c = ";
              cin>>a>>b>>c;                      //输入3条边长
              if(a+b>c&&b+c>a&&c+a>b)            //判断构成三角形的条件
                { s=(a+b+c)/2.0;                 //计算面积
                  area = sqrt(s * (s-a) * (s-b) * (s-c));
                  cout << "area = " << area << endl;
                }
              else
                  cout << "It is not a trilateral!" << endl;
            }

运行程序,输入数据后输出结果如下:

            a, b, c = 5 6 7
            area = 14.696939

在上述程序中,调用了C++标准库函数sqrt求平方根,函数原型为:

            double sqrt(double x);

数学运算的标准函数声明在C的标准头文件cmath中,详见附录B。

【例2-3】把输入字符转换为小写字母。对输入字符进行判断,如果是大写字母,则转换为小写字母;否则,不转换。

            #include <iostream>
            using namespace std;
            int main()
            {  char ch;
              cout << "ch = ";
              cin >> ch;
              if(ch>='A'&&ch<='Z')                //ch是大写字母
                  ch+=32;                         //转换成小写字母
              cout << ch << endl;
            }

【例2-4】求一元二次方程ax2 + bx + c = 0的根。

求一元二次方程的根的公式为:

编程时,要考虑如下各种情况。

① 当a=0 时,方程不是二次方程;

② 当 b2-4ac=0 时,有两个相同的实根:

③ 当 b2-4ac>0 时,有两个不同的实根:

④ 当 b2-4ac<0 时,有两个共轭复根:

按上述公式,编写程序如下:

            #include<iostream>
            #include<cmath>
            using namespace std;
            int main()
            {  double a,b,c,d,x1,x2,rp,ip;
              cout<<"a, b, c = ";
              cin>>a>>b>>c;                            //输入系数
              if(fabs(a)<=1e-8)                        //用误差判断,系数a等于0
                  cout<<" It is not quadratic."<<endl;
              else  {  d=b*b-4*a*c;                    //求判别式的值,赋给d
                      if(fabs(d)<=1e-8)                //d等于0,方程有两个相同的根
                          cout<<"It has two equal real roots: "<<-b/(2*a)<<endl;
                      else
                          if(d>1e-8)                   //d大于0,方程有两个不同的实根
                            {  x1=(-b+sqrt(d))/(2*a);
                              x2=(-b-sqrt(d))/(2*a);
                              cout<<"It has two distinct real roots: "
                                <<x1 <<" and "<<x2<<endl;
                            }
                        else                         //d小于0,方程有两个共轭复根
                          {  rp=-b/(2*a);
                            ip=sqrt(-d)/(2*a);
                            cout<<"It has two complex roots: "<<endl;
                            cout<<rp<<" + "<<ip<<"i"<<endl;
                            cout<<rp<<" - "<<ip<<"i"<<endl;
                          }
                  }
            }

程序中的条件:

            fabs(a)<=1e-8  和  fabs(d)<=1e-8

分别用来判断a和d的值是否为0。因为实数在计算和存储时会有微小的误差。若用“a==0”和“d==0”来判断a和d是否为0,则可能出现本来a和d等于0,由于计算或存储误差而导致判断结果不成立的情况。

程序运行结果:

            a, b, c = 0 2 1
            It is not quadratic.
            a, b, c = 1 2 1
            It has two equal real roots: -1
            a, b, c = 1 5 1
            It has two distinct real roots: -0.208712 and -4.791288
            a, b, c = 2 3 4
            It has two complex roots:
            -0.75 + 1.198958i
            -0.75 - 1.198958i

2.1.2 switch语句

switch语句应用于根据一个整型表达式的不同值决定程序分支的情况。

1.switch语句的形式和执行流程

switch语句形式为:

            switch(表达式)
            { case  常量表达式 1 :语句 1
              case  常量表达式 2 :语句 2
              
              case  常量表达式 n :语句 n
              [default : 语句 n+1 ;]
            }

其中,“表达式”类型为整型、字符型或枚举型,不能为浮点型。“常量表达式”具有指定值,与“表达式”类型相同。default子句为可选项。

switch语句的执行流程如图2.3所示。

图2.3 switch语句的执行流程

在switch语句中,case和default只起语句标号作用。进入switch后,首先计算“表达式”的值,然后用这个值依次与case后的“常量表达式”的值进行比较。如果“表达式”的值等于某个“常量表达式 i”的值,则执行“语句 i”。如果“语句 i”之后还有语句,就继续执行“语句 i+1”至“语句 n+1”。如果找不到与“表达式”的值相等的case常量,则执行default指示的“语句 n+1”。

【例2-5】测试switch语句的执行流程。

            #include <iostream>
            using namespace std;
            int main()
            {  int x;
              cout << "x = ";
              cin >> x;
              switch(x)
              { case 1:cout<<"one  ";
                  case 2:cout<<"two  ";
                  case 3:cout<<"three  ";
                  default:cout<<"other  ";
              }
              cout << "end" << endl;
            }

运行程序,若输入x的值为1,则输出结果如下:

            x = 1
            one  two  three  other  end

若重新运行,输入x的值为3,则输出结果如下:

            x = 3
            three  other  end

要实现真正的选择控制,执行一个case标号语句后能够跳出switch语句块,转向执行后续语句,应该使用break语句。break语句强制中断一个语句块的执行,转向执行语句块的后续语句。

【例2-6】测试在switch的执行语句中增加break语句,中断语句块。

            #include <iostream>
            using namespace std;
            int main()
            {  int x;
              cout << "x = ";
              cin >> x;
              switch(x)
              { case 1:cout<<"one  ";  break;
                case 2:cout<<"two  ";  break;
                case 3:cout<<"three  ";  break;
                default:cout<<"other  ";
              }
              cout << "end" << endl;
            }

运行程序,输入x的值为2,输出结果如下:

            x = 2
            two  end

选择性地在case语句中使用break语句,可以实现多个case常量值执行同一个分支语句。

【例2-7】两个case常量值执行同一个分支语句。

            #include <iostream>
            using namespace std;
            int main()
            {  int x;
              cout << "x = ";
              cin >> x;
              switch(x)
              { case 1:
                  case 2:cout<<"one or two  ";  break;
                  case 3:cout<<"three  ";  break;
                  default:cout<<"other  ";
              }
              cout << "end" << endl;
            }

程序不管x的输入值是1还是2,都执行case 2后的语句。输入1时,程序输出结果为:

            x = 1
            one or two  end

switch语句的说明如下。

① 常量表达式必须互不相同,否则,会出现矛盾而引起错误。例如:

            switch (int(x))
            {  case  1:  y=1;  break;
              case  2:  y=x;  break;
              case  2:  y=x*x;  break;      //错误,case 2已经使用
              case  3:  y=x*x*x;  break;
            }

② 各个case和default出现的次序可以任意。在每个case分支都带有break的情况下,case的顺序不影响执行结果。

③ switch语句可以嵌套。

2.应用举例

【例2-8】根据x的值,按下式计算y的值。

从这个分段函数的自变量 x取值可以看出,当1≤x<2时,int(x)=1;当2≤x<4时,int(x)等于2或3;当4≤x<6,int(x)等于4或5。因此,可以用switch语句编写程序。

            #include <iostream>
            using namespace std;
            #include<cmath>
            int main()
            {  double x,y;
              cout << "x = ";
              cin >> x;
              switch (int(x))
              {  case 1:  y=3*x-5;  break;
                  case 2:
                  case 3:  y=2*cos(x)+1;  break;
                  case 4:
                  case 5:  y=sqrt(1+x*x);  break;
                  default:y=x*x-4*x+5;
                }
                cout<<"y="<<y<<endl;
            }

运行程序3次,输入不同的x值,输出结果如下:

            x = 1.5
            y = -0.5
            x = 3.3
            y = -0.97496
            x = 4.7
            y = 4.80521

【例2-9】输入年份和月份,输出该月的天数。

根据天文知识,每年的1、3、5、7、8、10和12月,每月有31天;每年的4、6、9和11月,每月有30天;若是闰年,则2月份为29天;若为平年,则2月份为28天。年份能被4整除,但不能被100整除,或者年份能被400整除的年份为闰年;否则,为平年。

            #include <iostream>
            using namespace std;
            int main()
            {  int year,month,days;
              cout << "year : ";
              cin >> year;
              cout << "month : ";
              cin >> month;
              switch(month)
              {  case 1:case 3:case 5:case 7:case 8:case 10:case 12:
                    days=31;  break;
                  case 4: case 6: case 9: case 11:
                    days=30;  break;
                  case 2:  if((year%4==0)&&(year%100!=0)||(year%400==0))
                            days = 29;
                        else  days=28;
              }
              cout << " days : " << days << endl;
            }

运行程序,输入数据和输出结果如下:

            year : 2008
            month : 5
            days : 31

【例 2-10】输入包含两个运算量和一个运算符(+、-、* 或 /)算术表达式,计算并输出运算结果。

            #include <iostream>
            using namespace std;
            int main()
            {  double operand1,operand2,result;
              char oper;
              cout << "input oprand1,operator and oprand2:";
              cin>>operand1>>oper>>operand2;  //输入表达式
              switch(oper)                    //根据运算符做选择计算
                {  case'+':  result=operand1+operand2;  break;
                  case'-':  result=operand1-operand2;  break;
                  case'*':  result=operand1*operand2;  break;
                  case'/':  result=operand1/operand2;  break;
                  default:  cout<<"input error!"<<endl;
                          goto L;             //非法运算符转向L入口的空语句
                }
               cout << operand1 << oper << operand2 << "=" << result << endl;
               L:;                          //空语句
            }

运行程序,输入数据和输出结果如下:

            input oprand1,operator and oprand2: 2+6
            2+6=8

当用户输入无效的运算符时,程序用goto语句转向标号为L的语句,而L指示的语句是一个空语句。goto语句的目的是在无效输入时,跳过正常的结果显示。